diff --git a/.all-contributorsrc b/.all-contributorsrc index fdcdb2c547..ced462c98b 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -86,6 +86,15 @@ "doc" ] }, + { + "login": "sudhirtibrewal", + "name": "Sudhir Tibrewal", + "avatar_url": "https://avatars1.githubusercontent.com/u/12657704?v=4", + "profile": "https://github.com/sudhirtibrewal", + "contributions": [ + "code" + ] + }, { "login": "tkstanczak", "name": "Tomasz Kajetan Stańczak", @@ -95,6 +104,15 @@ "code", "ideas" ] + }, + { + "login": "Scooletz", + "name": "Szymon Kulec", + "avatar_url": "https://avatars1.githubusercontent.com/u/519707?v=4", + "profile": "http://blog.scooletz.com/", + "contributions": [ + "code" + ] } ], "contributorsPerLine": 7, diff --git a/.gitignore b/.gitignore index 91c5596f67..25eba04ca8 100644 --- a/.gitignore +++ b/.gitignore @@ -351,3 +351,4 @@ Icon? Thumbs.db *.lock +src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/build/ diff --git a/.gitmodules b/.gitmodules index 2c3db79ead..db8df0d321 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,12 +1,9 @@ [submodule "submodules/nethermind"] path = submodules/nethermind url = https://github.com/catalyst-network/nethermind.git -[submodule "submodules/protocol-protobuffs"] - path = submodules/protocol-protobuffs - url = https://github.com/catalyst-network/protocol-protobuffs.git -[submodule "submodules/Cryptography.FFI.Rust"] - path = submodules/Cryptography.FFI.Rust - url = https://github.com/catalyst-network/Cryptography.FFI.Rust.git +[submodule "submodules/Protocol"] + path = submodules/Protocol + url = https://github.com/catalyst-network/Protocol.git [submodule "docs/articles"] path = docs/articles url = https://github.com/catalyst-network/Catalyst.Framework.wiki.git diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md index 77c6bc69e8..bb6a1c8b4e 100644 --- a/CODE_OF_CONDUCT.md +++ b/CODE_OF_CONDUCT.md @@ -32,7 +32,7 @@ This Code of Conduct applies both within project spaces and in public spaces whe ## [](https://github.com/catalyst-network/Community/Contributor-Code-of-Conduct/Contributor-Code-of-Conduct#enforcement)Enforcement -Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project lead at [nshcore@protonmail.ch](mailto:nshcore@protonmail.ch), which goes to @nshCore, or to [richard@maintainer.io](mailto:richard@maintainer.io), which goes to @RichardLit. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. +Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project lead at [darrenop@atlascity.io](mailto:darrenop@atlascity.io), which goes to @Switch1983, or to [richard@maintainer.io](mailto:richard@maintainer.io), which goes to @RichardLit. The project team will review and investigate all complaints, and will respond in a way that it deems appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. diff --git a/COPYING b/COPYING index a1adf298bd..24154979b4 100644 --- a/COPYING +++ b/COPYING @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/GUIDELINES.md b/GUIDELINES.md index 9756f43d4e..437f431267 100644 --- a/GUIDELINES.md +++ b/GUIDELINES.md @@ -60,11 +60,11 @@ British English is preferred over other dialects throughout the solution. You ca /// /// /// -/// +/// /// /// public PeerDiscovery(IDns dns, - IPeerRepository repository, + IPeerService service, IConfigurationRoot rootSection, ILogger logger) { diff --git a/README.md b/README.md index 4fe14765f2..c4baf48f16 100644 --- a/README.md +++ b/README.md @@ -1,24 +1,32 @@
ReDoc logo - ### Catalyst Framework - Full Stack Distributed Protocol Framework + ### Catalyst - Full Stack Distributed Protocol Framework -[![Discord](https://img.shields.io/discord/629667101774446593?color=blueviolet&label=discord)](https://discord.gg/anTP7xm) -[![Twitter Follow](https://img.shields.io/twitter/follow/catalystnetorg?style=social)](https://twitter.com/catalystnetorg) +[![Website](https://catalystnet.org/) [![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/catalystnet?style=social)](https://reddit.com/r/catalystnet)
-[![Build Status](https://dev.azure.com/catalyst-network/catalyst.framework/_apis/build/status/pr-tests-master?branchName=develop)](https://dev.azure.com/catalyst-network/catalyst.framework/_build/latest?definitionId=22&branchName=develop) -[![Codacy Badge](https://api.codacy.com/project/badge/Grade/08cb016d7489471eadd0192ce4d7b26e)](https://www.codacy.com/manual/catalyst-network/Catalyst.Framework?utm_source=github.com&utm_medium=referral&utm_content=catalyst-network/Catalyst.Framework&utm_campaign=Badge_Grade) -[![All Contributors](https://img.shields.io/badge/all_contributors-9-orange.svg?style=flat-square)](#contributors) +[![Build Status](https://dev.azure.com/catalyst-network/Catalyst/_apis/build/status/pr-tests-master?branchName=master)](https://dev.azure.com/catalyst-network/Catalyst/_build/latest?definitionId=22&branchName=master) +[![Codacy Badge](https://api.codacy.com/project/badge/Grade/08cb016d7489471eadd0192ce4d7b26e)](https://www.codacy.com/manual/catalyst-network/Catalyst?utm_source=github.com&utm_medium=referral&utm_content=catalyst-network/Catalyst&utm_campaign=Badge_Grade) +[![All Contributors](https://img.shields.io/badge/all_contributors-11-orange.svg?style=flat-square)](#contributors)

-Join us on our [Discord](https://discord.gg/anTP7xm) for any questions and discussions. + + +**Important Release Notes 31/03/2020** + +***Known UDP messaging issue*** +For the initial release of the Catalyst Network UDP is used as the peer to peer messaging protocol. This causes fragmentation of packages and thereby transactions over 1280Bytes are prone to packet loss as discussed in [Issue #909](https://github.com/catalyst-network/Catalyst/issues/909). Due to the DFS utilising a seperate TCP messaging system this does not affect files stored in the DFS including ledger state updates. However it will affect both simple and smart contract transactions. + +***MacOS node running issue*** +Currently there is an issue with running RocksDB on MacOS meaning that a node can not unfortunatly be run on MacOS. A fix is currently in progress and will be availiable iminently. + **Table of Contents** @@ -27,9 +35,10 @@ Join us on our [Discord](https://discord.gg/anTP7xm) for any questions and discu - [What is Catalyst.Node?](#what-is-catalystnode) - [Features](#features) - [Documentation](#documentation) -- [Install](#install) - - [Install the Rust Toolchain](#install-the-rust-toolchain) - - [Install Rust via the Rustup tool:](#install-rust-via-the-rustup-tool) +- [Quick Start Guide for Node](#quick-start-guide-for-node) + - [Guide for Windows](#guide-for-windows) + - [Guide for MacOS](#guide-for-macos) + - [Guide for Linux](#guide-for-linux) - [Contributors](#contributors) - [Contributing](#contributing) - [License](#license) @@ -53,121 +62,57 @@ Catalyst was designed by an experienced team of engineers and researchers who we ## Documentation -Our api docs can be found on our documentation site [https://catalyst-network.github.io/Catalyst.Framework/](https://catalyst-network.github.io/Catalyst.Framework) - -## Quick Start Guide - -This is a quick start guide for new and existing .Net developers - -#### 1. Install .Net - -Catalyst.Node works with .Net Core v2.2. You'll need to have the .Net SDK installed. - -If you don't have .Net Core installed you can follow the instructions for your platform bellow. - -- [Windows](https://dotnet.microsoft.com/download?initial-os=windows) -- [Linux](https://dotnet.microsoft.com/download?initial-os=linux) -- [macOS](https://dotnet.microsoft.com/download?initial-os=macos) - -#### 2. Install the Rust Toolchain - -Catalyst.Core uses our native [Rust BulletProof library](https://github.com/catalyst-network/Cryptography.FFI.Rust). In order to be able to build the solution, you will need to ensure that the [Rust](https://www.rust-lang.org/) toolchain is correctly installed on you machine. - -##### Unix environments - -Download and install `msbuild prebuild tasks` from [Rust](https://www.rust-lang.org/). MsBuild will then compile the Bulletproof library when you try to build the project. - -Then, make sure you install Rust using the rustup tool: - -```curl https://sh.rustup.rs -sSf | sh``` - -If ```rustc --version``` fails, restart your console to ensure changes to ```PATH``` have taken effect. - -Refer to the Rust Bulletproof library [repository](https://github.com/catalyst-network/Cryptography.FFI.Rust) for docs. If you have issues with this part of the installation, please raise them there. - -##### Windows environments -If you have not done so before, download and install the Microsoft Visual C++ Build Tools 2019 from https://visualstudio.microsoft.com/downloads/#build-tools-for-visual-studio-2019. Alternatively, if you are using Visual Studio, you should be able to modify your existing installation to add this feature. - -Go to https://www.rust-lang.org/tools/install, then download and execute `rustup-init.exe` - -#### 3. Clone the repository - -To clone the repository it is assumed you have Git installed. -If you do not, then follow the [Git install instructions](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) for Linux/Windows/macOS. - -> ##### Windows environment prerquisite - Enable long paths -> make sure that long paths are allowed in both windows and git -> - for windows -> follow the instructions at https://docs.microsoft.com/en-us/windows/win32/fileio/naming-a-file#enable-long-paths-in-windows-10-version-1607-and-later -> - for git : -> start a git terminal as administrator -> run the following command : `git config --system core.longpaths true` - -The clone command is: - -`git clone git@github.com:catalyst-network/Catalyst.Node.git ` - -#### 4. Clone git submodules - -We utilize git submodules for certain dependencies. You will need these to build the code base. To grab them, first navigate to the Catalyst.Node folder: - -`cd Catalyst.Node` - -Then initialize the submodules by running: - -`git submodule init` - -The clone then by running: - -`git submodule update` +Our api docs can be found on our documentation site [https://catalyst-network.github.io/Catalyst/api](https://catalyst-network.github.io/Catalyst/api) -#### 5. Install Nuget dependencies +Furtermore, our Technical White Paper is availiable [here](https://github.com/catalyst-network/whitepaper). This document explains the implementation of the Catalyst network. -Now we have our code and submodules, the next step is to install the dependencies from .Net's package manager Nuget. +## Quick Start Guide for Node -Navigate to the src folder: +In order to run a node on Catalyst some baisic knowledge is neeeded on how to access / use Command prompt or the terminal for your opperating system. This guide can be found here: -`cd src` +[Getting Access to Command Prompt / Terminal](https://github.com/catalyst-network/Catalyst/wiki/Getting-Access-to-Command-Prompt-or-Terminal) -Next restore the dependencies: +The node set up process for each opperating system, so ease of use we have seperated the guides according to the opperating system. -`dotnet restore` +#### Guide for Windows +[Run a TestNet node on Windows](https://github.com/catalyst-network/Catalyst/wiki/Run-a-POA-node-on-Windows) -#### 6. Build the solution +#### Guide for MacOS +[Run a TestNet node on MacOS](https://github.com/catalyst-network/Catalyst/wiki/Run-POA-Node-on-MacOS) -Still in the `src` folder: +#### Guide for Linux +[Run a TestNet node on Linux ](https://github.com/catalyst-network/Catalyst/wiki/Run-a-POA-node-on-Linux) -`dotnet build` +### Configuring the node -#### 7. Run the test suite +Once the above steps have been completed the node must be manually configured following: -To check all is good under the hood, you can run the test suite. If you're on Linux, you need to [Create a Self Signed Certificate](https://github.com/catalyst-network/Catalyst.Node/wiki/Create-a-Self-Signed-Certificate) +[How to configure a Catalyst POA node](https://github.com/catalyst-network/Catalyst/wiki/Configuring-a-Catalyst-POA-Node) -`dotnet test` -## Framework modules +## Modules | Core Libraries | Description | Nuget | |---------------------|---------------------------------------|-------| -| [Abstractions](https://catalyst-network.github.io/Catalyst.Framework/api/abstractions/Catalyst.Abstractions.html) | Framework Abstractions and interfaces | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Abstractions ) | -| [Core Lib](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Lib/Catalyst.Core.Lib.html) | Core Catalyst libraries | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Lib ) | -| [Protocol SDK](https://catalyst-network.github.io/Catalyst.Framework/api/Protocol/Catalyst.Protocol.Account.html) | Catalyst protocol c# sdk | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Protocol ) | +| [Abstractions](https://catalyst-network.github.io/Catalyst/api/abstractions/Catalyst.Abstractions.html) |Abstractions and interfaces | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Abstractions ) | +| [Core Lib](https://catalyst-network.github.io/Catalyst/api/Core.Lib/Catalyst.Core.Lib.html) | Core Catalyst libraries | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Lib ) | +| [Protocol SDK](https://catalyst-network.github.io/Catalyst/api/Protocol/Catalyst.Protocol.Account.html) | Catalyst protocol c# sdk | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Protocol ) | | **Core Modules** | **Description** | **Nuget** | -| [Kvm](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/Kvm/Catalyst.Core.Modules.Kvm.html) | Finite state machine for smart contacts | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Kvm) | -| [Mempool](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/Mempool/Catalyst.Core.Modules.Mempool.html) | Deterministic mempool for ordering transactions | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Mempool ) | -| [Web3](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/Web3/Catalyst.Core.Modules.Web3.html) | Web3 gaateway | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Web3) | -| [Consensus](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/Consensus/Catalyst.Core.Modules.Consensus.html) | PBFT Consensus Mechanism | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Consensus ) | -| [Dfs](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/Dfs/Catalyst.Core.Modules.Dfs.html) | Distributed File Storage | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Dfs ) | -| [Keystore](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/Keystore/Catalyst.Core.Modules.Keystore.html) | Secure Keystore | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Keystore ) | -| [Hastings Discovery](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/P2P.Discovery.Hastings/Catalyst.Core.Modules.P2P.Discovery.Hastings.html) | Unstructured overlay network with metropolis hasting random walk | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.P2P.Discovery.Hastings) | -| [Rpc Server](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/Rpc.Server/Catalyst.Core.Modules.Rpc.Server.html) | Rpc Server pipeline with dotnetty | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Rpc.Server) | -| [Rpc Client](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/Rpc.Client/Catalyst.Core.Modules.Rpc.Client.html) | Rpc Client pipeline with dotnetty | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Rpc.Client ) | -| [KeySigner](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/KeySigner/Catalyst.Core.Modules.KeySigner.html) | Catalyst KeySigner, sign transactions and message with identity and context | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.KeySigner ) | -| [Ledger](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/Ledger/Catalyst.Core.Modules.Ledger.html) | Catalyst ledger state provider | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Ledger ) | -| [BulletProofs Cryptography](https://catalyst-network.github.io/Catalyst.Framework/api/Core.Modules/Cryptography.BulletProofs/Catalyst.Core.Modules.Cryptography.BulletProofs.html) | Bullet proof native rust bindings | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Cryptography.BulletProofs) | +| [Kvm](https://catalyst-network.github.io/Catalyst/api/Core.Modules/Kvm/Catalyst.Core.Modules.Kvm.html) | Finite state machine for smart contacts | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Kvm) | +| [Mempool](https://catalyst-network.github.io/Catalyst/api/Core.Modules/Mempool/Catalyst.Core.Modules.Mempool.html) | Deterministic mempool for ordering transactions | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Mempool ) | +| [Web3](https://catalyst-network.github.io/Catalyst/api/Core.Modules/Web3/Catalyst.Core.Modules.Web3.html) | Web3 gaateway | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Web3) | +| [Consensus](https://catalyst-network.github.io/Catalyst/api/Core.Modules/Consensus/Catalyst.Core.Modules.Consensus.html) | PBFT Consensus Mechanism | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Consensus ) | +| [Dfs](https://catalyst-network.github.io/Catalyst/api/Core.Modules/Dfs/Catalyst.Core.Modules.Dfs.html) | Distributed File Storage | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Dfs ) | +| [Keystore](https://catalyst-network.github.io/Catalyst/api/Core.Modules/Keystore/Catalyst.Core.Modules.Keystore.html) | Secure Keystore | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Keystore ) | +| [Hastings Discovery](https://catalyst-network.github.io/Catalyst/api/Core.Modules/P2P.Discovery.Hastings/Catalyst.Core.Modules.P2P.Discovery.Hastings.html) | Unstructured overlay network with metropolis hasting random walk | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.P2P.Discovery.Hastings) | +| [Rpc Server](https://catalyst-network.github.io/Catalyst/api/Core.Modules/Rpc.Server/Catalyst.Core.Modules.Rpc.Server.html) | Rpc Server pipeline with dotnetty | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Rpc.Server) | +| [Rpc Client](https://catalyst-network.github.io/Catalyst/api/Core.Modules/Rpc.Client/Catalyst.Core.Modules.Rpc.Client.html) | Rpc Client pipeline with dotnetty | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Rpc.Client ) | +| [KeySigner](https://catalyst-network.github.io/Catalyst/api/Core.Modules/KeySigner/Catalyst.Core.Modules.KeySigner.html) | Catalyst KeySigner, sign transactions and message with identity and context | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.KeySigner ) | +| [Ledger](https://catalyst-network.github.io/Catalyst/api/Core.Modules/Ledger/Catalyst.Core.Modules.Ledger.html) | Catalyst ledger state provider | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Ledger ) | +| [BulletProofs Cryptography](https://catalyst-network.github.io/Catalyst/api/Core.Modules/Cryptography.BulletProofs/Catalyst.Core.Modules.Cryptography.BulletProofs.html) | Bullet proof native rust bindings | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Core.Modules.Cryptography.BulletProofs) | | **Optional Modules** | **Description** | **Nuget** | -| [CosmosDB](https://catalyst-network.github.io/Catalyst.Framework/api/Modules/Repository.CosmosDb/Catalyst.Modules.Repository.CosmosDb.html) | Azure CosmosDb connector | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Modules.Repository.CosmosDb ) | -| [MongoDB](https://catalyst-network.github.io/Catalyst.Framework/api/Modules/Repository.MongoDb/Catalyst.Modules.Repository.MongoDb.html) | MongoDb connector | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Modules.Repository.MongoDb ) | +| [CosmosDB](https://catalyst-network.github.io/Catalyst/api/Modules/Repository.CosmosDb/Catalyst.Modules.Repository.CosmosDb.html) | Azure CosmosDb connector | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Modules.Repository.CosmosDb ) | +| [MongoDB](https://catalyst-network.github.io/Catalyst/api/Modules/Repository.MongoDb/Catalyst.Modules.Repository.MongoDb.html) | MongoDb connector | ![Nuget](https://img.shields.io/nuget/v/Catalyst.Modules.Repository.MongoDb ) | ## Contributors Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): @@ -176,17 +121,19 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d - - - - - - - + + + + + + + - - + + + +
nshCore
nshCore

🚇 🤔 ⚠️ 💻
franssl
franssl

⚠️ 💻
monsieurleberre
monsieurleberre

⚠️ 💻
atlassanjay
atlassanjay

⚠️ 💻
Millymanz
Millymanz

⚠️ 💻
Richard Schneider
Richard Schneider

⚠️ 💻
Alex
Alex

💻 ⚠️
nshCore
nshCore

🚇 🤔 ⚠️ 💻
franssl
franssl

⚠️ 💻
monsieurleberre
monsieurleberre

⚠️ 💻
atlassanjay
atlassanjay

⚠️ 💻
Millymanz
Millymanz

⚠️ 💻
Richard Schneider
Richard Schneider

⚠️ 💻
Alex
Alex

💻 ⚠️
Richard Littauer
Richard Littauer

📖
Tomasz Kajetan Stańczak
Tomasz Kajetan Stańczak

💻 🤔
Richard Littauer
Richard Littauer

📖
sudhirtibrewal
Sudhir Tibrewal

💻
Tomasz Kajetan Stańczak
Tomasz Kajetan Stańczak

💻 🤔
Szymon Kulec
Szymon Kulec

💻
@@ -198,10 +145,10 @@ Now that you've seen all of the contributors, why not contribute? We're always k **Take a look at our organization-wide [Contributing Guide](https://github.com/catalyst-network/Community/blob/master/CONTRIBUTING.md).** You'll find most of your questions answered there. -As far as code goes, we would be happy to accept PRs! If you want to work on something, it'd be good to talk beforehand to make sure nobody else is working on it. You can reach us [on Discord](https://discord.gg/anTP7xm), or in the [issues section](https://github.com/catalyst-network/Catalyst.Node/issues). +As far as code goes, we would be happy to accept PRs! If you want to work on something, it'd be good to talk beforehand to make sure nobody else is working on it. You can reach us in the [issues section](https://github.com/catalyst-network/Catalyst.Node/issues). Please note that we have a [Code of Conduct](CODE_OF_CONDUCT.md), and that all activity in the [@catalyst-network](https://github.com/catalyst-network) organization falls under it. Read it when you get the chance, as being part of this community means that you agree to abide by it. Thanks. ## License -[GPL](LICENSE) © 2019 Catalyst Network +[GPL](LICENSE) © 2022 Catalyst Network diff --git a/SECURITY.md b/SECURITY.md index e92199df31..f6448e2e65 100644 --- a/SECURITY.md +++ b/SECURITY.md @@ -10,12 +10,5 @@ Currently supported versions ## Reporting a Vulnerability -To report security issues send an email to info@catalystnet.org (not for support). +To report security issues send an email to info@catalystnet.org (not for support) or [darren.op@catalystnet.org](mailto:darren.op@catalystnet.org), which goes to @TheNewAutonomy. -The following keys may be used to communicate sensitive information to developers: - -| Name | Fingerprint | -|------|-------------| -| NshCore | 32BF 3146 B10F B31A 1B8C 861F 53D2 EAC7 1741 C4C0 | - -You can import a key by running the following command with that individual’s fingerprint: `gpg --recv-keys ""` Ensure that you put quotes around fingerprints containing spaces. diff --git a/docs/Catalyst.Documentation.csproj b/docs/Catalyst.Documentation.csproj index c248c0e6e7..a2ab184926 100644 --- a/docs/Catalyst.Documentation.csproj +++ b/docs/Catalyst.Documentation.csproj @@ -1,7 +1,7 @@  - netcoreapp3.0 + net6.0 @@ -9,35 +9,35 @@ - <_ResolveComReferenceCache Remove="obj\Debug\netcoreapp3.0\Catalyst.Documentation.csproj.ResolveComReference.cache" /> + <_ResolveComReferenceCache Remove="obj\Debug\netcoreapp3.1\Catalyst.Documentation.csproj.ResolveComReference.cache" /> - + - <_DebugSymbolsIntermediatePath Remove="obj\Debug\netcoreapp3.0\Catalyst.Documentation.pdb" /> + <_DebugSymbolsIntermediatePath Remove="obj\Debug\netcoreapp3.1\Catalyst.Documentation.pdb" /> - <_DeploymentManifestEntryPoint Remove="obj\Debug\netcoreapp3.0\Catalyst.Documentation.dll" /> + <_DeploymentManifestEntryPoint Remove="obj\Debug\netcoreapp3.1\Catalyst.Documentation.dll" /> - + - + - + - + diff --git a/docs/docfx.json b/docs/docfx.json index 4dcb4d40b6..ca8af16527 100644 --- a/docs/docfx.json +++ b/docs/docfx.json @@ -361,7 +361,7 @@ "ExtractSearchIndex" ], "globalMetadata": { - "_appFooter": "Copyright © 2019 Catalyst Network", + "_appFooter": "Copyright © 2022 Catalyst Network", "_appTitle": "Catalyst Framework Documentation", "_enableSearch": "true", "_appLogoPath": "images/logo.svg", diff --git a/mssql-e2e-azure-pipelines.yml b/mssql-e2e-azure-pipelines.yml index 8904f3e8cb..514292a821 100644 --- a/mssql-e2e-azure-pipelines.yml +++ b/mssql-e2e-azure-pipelines.yml @@ -58,6 +58,6 @@ steps: inputs: command: test projects: 'src/*[Tt]ests/*.csproj' - arguments: '--filter TestType=EndToEndTest_MSSQL --configuration $(BuildConfiguration)' + arguments: '--filter TestCategory=EndToEndTest_MSSQL --configuration $(BuildConfiguration)' failOnStandardError: 'true' timeoutInMinutes: 10 \ No newline at end of file diff --git a/nuget-pre-release-azure-pipelines.yml b/nuget-pre-release-azure-pipelines.yml index 58139b00b8..e41e8674a5 100644 --- a/nuget-pre-release-azure-pipelines.yml +++ b/nuget-pre-release-azure-pipelines.yml @@ -26,7 +26,7 @@ stages: - publish: '$(Build.ArtifactStagingDirectory)/$(ArtifactName)' artifact: $(ArtifactName) - stage: 'BuildNativeFFI' - dependsOn: 'GetSource' + dependsOn: 'GetSaurce' condition: succeeded() jobs: - job: 'BuildBulletProofsFFI' diff --git a/poa-azure-pipeline.yml b/poa-azure-pipeline.yml index 8e0af77e4d..230af1d13f 100644 --- a/poa-azure-pipeline.yml +++ b/poa-azure-pipeline.yml @@ -2,16 +2,13 @@ strategy: matrix: Windows-VS2019: imageName: 'windows-2019' - netcore_sdk: 3.0.100 - rustup_toolchain: stable + netcore_sdk: 3.1.201 Osx-Mojave: imageName: 'macos-10.14' - netcore_sdk: 3.0.100 - rustup_toolchain: stable + netcore_sdk: 3.1.201 Ubuntu-1604: imageName: 'ubuntu-16.04' - netcore_sdk: 3.0.100 - rustup_toolchain: stable + netcore_sdk: 3.1.201 maxParallel: 3 variables: @@ -62,27 +59,22 @@ steps: - task: DotNetCoreInstaller@0 displayName: 'Install .NetCore SDK' inputs: - version: 3.0.100 + version: 3.1.201 - bash: | git submodule update --init --force --recursive displayName: 'Clone submodules' - - script: | - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUSTUP_TOOLCHAIN - echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin" - displayName: Install rust - condition: ne( variables['Agent.OS'], 'Windows_NT' ) - - script: | - curl -sSf -o rustup-init.exe https://win.rustup.rs - rustup-init.exe -y --default-toolchain %RUSTUP_TOOLCHAIN% - echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin" - displayName: Windows install rust - condition: eq( variables['Agent.OS'], 'Windows_NT' ) + - task: DotNetCoreCLI@2 + displayName: 'Restore packages' + inputs: + command: restore + projects: 'src/*[Tt]ests/*.csproj' + - task: DotNetCoreCLI@2 displayName: 'Build solution' inputs: - projects: 'src/**/*.csproj' + projects: 'src/*[Tt]ests/*.csproj' arguments: '--configuration $(BuildConfiguration)' failOnStandardError: 'true' @@ -97,7 +89,7 @@ steps: projects: 'src/*[Tt]ests/*.csproj' arguments: '--filter TestType!=IntegrationTest --configuration $(BuildConfiguration)' failOnStandardError: 'true' - timeoutInMinutes: 10 + timeoutInMinutes: 20 condition: ne( variables['Agent.OS'], 'Windows_NT' ) - task: DotNetCoreCLI@2 @@ -107,7 +99,7 @@ steps: projects: 'src/*[Tt]ests/*.csproj' arguments: '--filter TestType!=IntegrationTest --configuration $(BuildConfiguration) /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:UseSourceLink=true /p:Include="\"[Catalyst.Core.Lib*]Catalyst.Core.Lib*\"" /p:Threshold=0 /p:ThresholdType=line /p:ThresholdStat=total' failOnStandardError: 'true' - timeoutInMinutes: 10 + timeoutInMinutes: 20 condition: eq( variables['Agent.OS'], 'Windows_NT' ) - task: DotNetCoreCLI@2 @@ -117,7 +109,7 @@ steps: projects: 'src/*[Tt]ests/*.csproj' arguments: '--filter TestType=IntegrationTest --configuration $(BuildConfiguration)' failOnStandardError: 'true' - timeoutInMinutes: 10 + timeoutInMinutes: 20 - script: | reportgenerator "-reports:src/**/coverage.cobertura.xml" "-targetdir:$(Build.ArtifactStagingDirectory)" -reporttypes:HtmlInline_AzurePipelines;Cobertura;SonarQube;Badges "-assemblyfilters:+Catalyst.Core.Lib*;+Catalyst.Core.Modules.Kvm*;" @@ -137,4 +129,4 @@ steps: summaryFileLocation: '$(Build.ArtifactStagingDirectory)/Cobertura.xml' reportDirectory: '$(Build.ArtifactStagingDirectory)' failOnStandardError: 'true' - condition: eq( variables['Agent.OS'], 'Windows_NT' ) + condition: eq( variables['Agent.OS'], 'Windows_NT' ) \ No newline at end of file diff --git a/pr-test-suite-azure-pipelines.yml b/pr-test-suite-azure-pipelines.yml index 18ff44dc5a..9e495a2cf9 100644 --- a/pr-test-suite-azure-pipelines.yml +++ b/pr-test-suite-azure-pipelines.yml @@ -1,25 +1,14 @@ strategy: matrix: Windows-VS2019: - imageName: 'windows-2019' - netcore_sdk: 3.0.100 - rustup_toolchain: stable - Osx-Sierra: - imageName: 'macos-10.13' - netcore_sdk: 3.0.100 - rustup_toolchain: stable + imageName: 'vs2017-win2016' + netcore_sdk: 3.1.201 Osx-Mojave: imageName: 'macos-10.14' - netcore_sdk: 3.0.100 - rustup_toolchain: stable + netcore_sdk: 3.1.201 Ubuntu-1804: imageName: 'ubuntu-18.04' - netcore_sdk: 3.0.100 - rustup_toolchain: stable - Ubuntu-1604: - imageName: 'ubuntu-16.04' - netcore_sdk: 3.0.100 - rustup_toolchain: stable + netcore_sdk: 3.1.201 maxParallel: 3 variables: @@ -30,24 +19,8 @@ variables: pool: vmImage: $(imageName) -trigger: - batch: true - branches: - include: - - develop - exclude: - - master/* - - release/* - pr: - autoCancel: true - branches: - include: - - develop - - feature/* - exclude: - - master/* - - release/* + - develop schedules: - cron: "0 0 * * *" @@ -64,30 +37,29 @@ steps: - task: DotNetCoreInstaller@0 displayName: 'Install .NetCore SDK' inputs: - version: 3.0.100 + version: 3.1.201 failOnStandardError: 'true' - - - bash: | - git submodule update --init --force --recursive - displayName: 'Clone submodules' - + - script: | - curl https://sh.rustup.rs -sSf | sh -s -- -y --default-toolchain $RUSTUP_TOOLCHAIN - echo "##vso[task.setvariable variable=PATH;]$PATH:$HOME/.cargo/bin" - displayName: Install rust - condition: ne( variables['Agent.OS'], 'Windows_NT' ) - + sudo apt-get install libsnappy-dev + displayName: Install snappy + condition: eq( variables['Agent.OS'], 'Linux' ) + - script: | - curl -sSf -o rustup-init.exe https://win.rustup.rs - rustup-init.exe -y --default-toolchain %RUSTUP_TOOLCHAIN% - echo "##vso[task.setvariable variable=PATH;]%PATH%;%USERPROFILE%\.cargo\bin" - displayName: Windows install rust - condition: eq( variables['Agent.OS'], 'Windows_NT' ) - + brew install snappy + displayName: Install snappy + condition: eq( variables['Agent.OS'], 'Darwin' ) + + - task: DotNetCoreCLI@2 + displayName: 'Restore packages' + inputs: + command: restore + projects: 'src/*[Tt]ests/*.csproj' + - task: DotNetCoreCLI@2 displayName: 'Build solution' inputs: - projects: 'src/**/*.csproj' + projects: 'src/*[Tt]ests/*.csproj' arguments: '--configuration $(BuildConfiguration)' failOnStandardError: 'true' @@ -100,9 +72,9 @@ steps: inputs: command: test projects: 'src/*[Tt]ests/*.csproj' - arguments: '--filter TestType!=IntegrationTest --configuration $(BuildConfiguration)' + arguments: '--filter TestCategory!=IntegrationTest --configuration $(BuildConfiguration)' failOnStandardError: 'true' - timeoutInMinutes: 10 + timeoutInMinutes: 20 condition: ne( variables['Agent.OS'], 'Windows_NT' ) - task: DotNetCoreCLI@2 @@ -110,9 +82,9 @@ steps: inputs: command: test projects: 'src/*[Tt]ests/*.csproj' - arguments: '--filter TestType!=IntegrationTest --configuration $(BuildConfiguration) /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:UseSourceLink=true /p:Include="\"[Catalyst.Core.Lib*]Catalyst.Core.Lib*\"" /p:Threshold=0 /p:ThresholdType=line /p:ThresholdStat=total' + arguments: '--filter TestCategory!=IntegrationTest --configuration $(BuildConfiguration) /p:CollectCoverage=true /p:CoverletOutputFormat=cobertura /p:UseSourceLink=true /p:Exclude="\"[Catalyst.Tools*]Catalyst.Tools*[Catalyst.Protocol*]Catalyst.Protocol*,[Catalyst.Simulator*]Catalyst.Simulator*,[Catalyst.TestUtils*]Catalyst.TestUtils*\"" /p:Include="\"[Catalyst.Core.*]Catalyst.Core.*,[Catalyst.Modules.*]Catalyst.Modules.*\"" /p:Threshold=0 /p:ThresholdType=line /p:ThresholdStat=total' failOnStandardError: 'true' - timeoutInMinutes: 10 + timeoutInMinutes: 20 condition: eq( variables['Agent.OS'], 'Windows_NT' ) - task: DotNetCoreCLI@2 @@ -120,12 +92,12 @@ steps: inputs: command: test projects: 'src/*[Tt]ests/*.csproj' - arguments: '--filter TestType=IntegrationTest --configuration $(BuildConfiguration)' + arguments: '--filter TestCategory=IntegrationTest --configuration $(BuildConfiguration)' failOnStandardError: 'true' - timeoutInMinutes: 10 + timeoutInMinutes: 20 - script: | - reportgenerator "-reports:src/**/coverage.cobertura.xml" "-targetdir:$(Build.ArtifactStagingDirectory)" -reporttypes:HtmlInline_AzurePipelines;Cobertura;SonarQube;Badges "-assemblyfilters:+Catalyst.Core.Lib*;+Catalyst.Core.Modules.Kvm*;" + reportgenerator "-reports:src/**/coverage.cobertura.xml" "-targetdir:$(Build.ArtifactStagingDirectory)" -reporttypes:HtmlInline_AzurePipelines;Cobertura;SonarQube;Badges "-assemblyfilters:+Catalyst.Core.*;+Catalyst.Modules.*;" displayName: 'Generate unit test coverage' condition: eq( variables['Agent.OS'], 'Windows_NT' ) - task: PublishBuildArtifacts@1 diff --git a/src/Catalyst.Abstractions/Attributes/IAuditable.cs b/src/Catalyst.Abstractions/Attributes/IAuditable.cs index 44dddfd834..d4b4004139 100644 --- a/src/Catalyst.Abstractions/Attributes/IAuditable.cs +++ b/src/Catalyst.Abstractions/Attributes/IAuditable.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj b/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj index 8e3879ae9f..b9fd765c7a 100644 --- a/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj +++ b/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj @@ -1,32 +1,40 @@ - - - netcoreapp3.0 - Catalyst.Abstractions - James Kirby (nshcore@protonmail.com) - true - Catalyst.Abstractions.snk - true - latest - true - - - - - - - - - - - - - - - - - - - - - + + + net6.0 + Catalyst.Abstractions + Darren Priestnall (darren.op@catalystnet.org) + true + Catalyst.Abstractions.snk + true + latest + true + + + 1701;1702;CS8002 + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Catalyst.Abstractions/Cli/ICatalystCli.cs b/src/Catalyst.Abstractions/Cli/ICatalystCli.cs index 715fe690cc..40eb6675ab 100644 --- a/src/Catalyst.Abstractions/Cli/ICatalystCli.cs +++ b/src/Catalyst.Abstractions/Cli/ICatalystCli.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/IUserInput.cs b/src/Catalyst.Abstractions/Cli/IUserInput.cs index d33fcc5bed..5c4129531a 100644 --- a/src/Catalyst.Abstractions/Cli/IUserInput.cs +++ b/src/Catalyst.Abstractions/Cli/IUserInput.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/IUserOutput.cs b/src/Catalyst.Abstractions/Cli/IUserOutput.cs index 5a051e1c7c..45cb93fe9e 100644 --- a/src/Catalyst.Abstractions/Cli/IUserOutput.cs +++ b/src/Catalyst.Abstractions/Cli/IUserOutput.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IAddFileOnDfsOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IAddFileOnDfsOptions.cs index fe19e52d4f..800c861837 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IAddFileOnDfsOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IAddFileOnDfsOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IConnectOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IConnectOptions.cs index d6937f50f6..02bc283f67 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IConnectOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IConnectOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IDisconnectOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IDisconnectOptions.cs index 3ae227416f..75f9c9f129 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IDisconnectOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IDisconnectOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IGetFileOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IGetFileOptions.cs index d9be6b11d1..d383035fda 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IGetFileOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IGetFileOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IGetInfoOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IGetInfoOptions.cs index 415d802301..bfb0533eec 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IGetInfoOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IGetInfoOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IGetMempoolOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IGetMempoolOptions.cs index 745ed571f6..6659dfdfeb 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IGetMempoolOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IGetMempoolOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IGetPeerInfoOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IGetPeerInfoOptions.cs index dd8d30e7e2..88096a963b 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IGetPeerInfoOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IGetPeerInfoOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -34,19 +34,11 @@ public interface IGetPeerInfoOptions string Node { get; set; } /// - /// Gets or sets the ip address. + /// Gets or sets the address. /// /// - /// The ip address. + /// The multi address. /// - string IpAddress { get; set; } - - /// - /// Gets or sets the public key. - /// - /// - /// The public key. - /// - string PublicKey { get; set; } + public string Address { get; set; } } } diff --git a/src/Catalyst.Abstractions/Cli/Options/IGetVersionOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IGetVersionOptions.cs index f48cba20d5..910467e16e 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IGetVersionOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IGetVersionOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IOptionsBase.cs b/src/Catalyst.Abstractions/Cli/Options/IOptionsBase.cs index 7d7b3b8ddc..2168b2c816 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IOptionsBase.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IOptionsBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IPeerBlackListingOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IPeerBlackListingOptions.cs index e40362404a..9e93ad30ac 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IPeerBlackListingOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IPeerBlackListingOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -35,19 +35,11 @@ public interface IPeerBlackListingOptions : IOptionsBase bool BlackListFlag { get; set; } /// - /// Gets or sets the ip address. + /// Gets or sets the address. /// /// - /// The ip address. + /// The multi address. /// - string IpAddress { get; set; } - - /// - /// Gets or sets the public key. - /// - /// - /// The public key. - /// - string PublicKey { get; set; } + public string Address { get; set; } } } diff --git a/src/Catalyst.Abstractions/Cli/Options/IPeerCountOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IPeerCountOptions.cs index b4cbe4fc0d..5cda206a70 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IPeerCountOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IPeerCountOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IPeerListOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IPeerListOptions.cs index 85423bbd40..f6f330e0e4 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IPeerListOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IPeerListOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IPeerReputationOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IPeerReputationOptions.cs index 62adb23b30..1a56056a21 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IPeerReputationOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IPeerReputationOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,8 +25,6 @@ namespace Catalyst.Abstractions.Cli.Options { public interface IPeerReputationOptions : IOptionsBase { - string IpAddress { get; set; } - - string PublicKey { get; set; } + public string Address { get; set; } } } diff --git a/src/Catalyst.Abstractions/Cli/Options/IRemovePeerOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IRemovePeerOptions.cs index 882f95f114..a3c1b5b2a3 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IRemovePeerOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IRemovePeerOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,12 +25,8 @@ namespace Catalyst.Abstractions.Cli.Options { public interface IRemovePeerOptions : IOptionsBase { - /// Gets or sets the public key. - /// The public key. - string PublicKey { get; set; } - - /// Gets or sets the ip. - /// The ip. - string Ip { get; set; } + /// Gets or sets the address. + /// The multi address. + public string Address { get; set; } } } diff --git a/src/Catalyst.Abstractions/Cli/Options/ISignOptions.cs b/src/Catalyst.Abstractions/Cli/Options/ISignOptions.cs index f094e2eaec..ff0f4c210c 100644 --- a/src/Catalyst.Abstractions/Cli/Options/ISignOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/ISignOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cli/Options/IVerifyOptions.cs b/src/Catalyst.Abstractions/Cli/Options/IVerifyOptions.cs index 3c60a470fb..c2c693fec2 100644 --- a/src/Catalyst.Abstractions/Cli/Options/IVerifyOptions.cs +++ b/src/Catalyst.Abstractions/Cli/Options/IVerifyOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Config/IConfigCopier.cs b/src/Catalyst.Abstractions/Config/IConfigCopier.cs index 05d3aa2de1..573b997966 100644 --- a/src/Catalyst.Abstractions/Config/IConfigCopier.cs +++ b/src/Catalyst.Abstractions/Config/IConfigCopier.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -37,7 +37,7 @@ public interface IConfigCopier /// Should config existing config files be overwritten by default? /// void RunConfigStartUp(string dataDir, - NetworkType networkType = NetworkType.Devnet, + NetworkType networkType, string sourceFolder = null, bool overwrite = false, string overrideNetworkFile = null); diff --git a/src/Catalyst.Abstractions/Repository/IRepositoryWrapper.cs b/src/Catalyst.Abstractions/Config/IDeltaConfig.cs similarity index 79% rename from src/Catalyst.Abstractions/Repository/IRepositoryWrapper.cs rename to src/Catalyst.Abstractions/Config/IDeltaConfig.cs index d910bb6124..2058047a4f 100644 --- a/src/Catalyst.Abstractions/Repository/IRepositoryWrapper.cs +++ b/src/Catalyst.Abstractions/Config/IDeltaConfig.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,10 @@ #endregion -using SharpRepository.Repository; - -namespace Catalyst.Abstractions.Repository +namespace Catalyst.Abstractions.Config { - public interface IRepositoryWrapper : IRepository where T : class { } + public interface IDeltaConfig + { + ulong DeltaGasLimit { get; } + } } diff --git a/src/Catalyst.Abstractions/Config/IEnumerableMessageType.cs b/src/Catalyst.Abstractions/Config/IEnumerableMessageType.cs index 164a78fca0..14ff5cf210 100644 --- a/src/Catalyst.Abstractions/Config/IEnumerableMessageType.cs +++ b/src/Catalyst.Abstractions/Config/IEnumerableMessageType.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Kvm/IStateRootResolver.cs b/src/Catalyst.Abstractions/Config/INetworkTypeProvider.cs similarity index 78% rename from src/Catalyst.Abstractions/Kvm/IStateRootResolver.cs rename to src/Catalyst.Abstractions/Config/INetworkTypeProvider.cs index 3612d867d5..879b196bb4 100644 --- a/src/Catalyst.Abstractions/Kvm/IStateRootResolver.cs +++ b/src/Catalyst.Abstractions/Config/INetworkTypeProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,12 +21,12 @@ #endregion -using Nethermind.Core.Crypto; +using Catalyst.Protocol.Network; -namespace Catalyst.Abstractions.Kvm +namespace Catalyst.Abstractions.Config { - public interface IStateRootResolver + public interface INetworkTypeProvider { - Keccak Resolve(Keccak deltaHash); // or multihash or whatever + NetworkType NetworkType { get; } } } diff --git a/src/Catalyst.Abstractions/Config/IReputationEvents.cs b/src/Catalyst.Abstractions/Config/IReputationEvents.cs index 1f05f2fb1b..81389dd74a 100644 --- a/src/Catalyst.Abstractions/Config/IReputationEvents.cs +++ b/src/Catalyst.Abstractions/Config/IReputationEvents.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Config/ITransactionConfig.cs b/src/Catalyst.Abstractions/Config/ITransactionConfig.cs new file mode 100644 index 0000000000..ac85ef5015 --- /dev/null +++ b/src/Catalyst.Abstractions/Config/ITransactionConfig.cs @@ -0,0 +1,30 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.Config +{ + public interface ITransactionConfig + { + ulong MinTransactionEntryGasLimit { get; } + } +} diff --git a/src/Catalyst.Abstractions/Config/IValidatorSetConfig.cs b/src/Catalyst.Abstractions/Config/IValidatorSetConfig.cs new file mode 100644 index 0000000000..4fb96efa7e --- /dev/null +++ b/src/Catalyst.Abstractions/Config/IValidatorSetConfig.cs @@ -0,0 +1,34 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Validators; +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace Catalyst.Abstractions.Config +{ + public interface IValidatorSetConfig + { + Task> GetValidatorSetsAsync(); + } +} diff --git a/src/Catalyst.Abstractions/Consensus/Cycle/ICycleConfiguration.cs b/src/Catalyst.Abstractions/Consensus/Cycle/ICycleConfiguration.cs index d21457e3e5..849e70822c 100644 --- a/src/Catalyst.Abstractions/Consensus/Cycle/ICycleConfiguration.cs +++ b/src/Catalyst.Abstractions/Consensus/Cycle/ICycleConfiguration.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Consensus/Cycle/ICycleEventsProvider.cs b/src/Catalyst.Abstractions/Consensus/Cycle/ICycleEventsProvider.cs index 36c3028c8b..56a3302394 100644 --- a/src/Catalyst.Abstractions/Consensus/Cycle/ICycleEventsProvider.cs +++ b/src/Catalyst.Abstractions/Consensus/Cycle/ICycleEventsProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,6 +22,7 @@ #endregion using System; +using System.Threading.Tasks; namespace Catalyst.Abstractions.Consensus.Cycle { @@ -29,7 +30,7 @@ namespace Catalyst.Abstractions.Consensus.Cycle /// Use this service to get notification about the different events happening during the /// delta production cycles. /// - public interface ICycleEventsProvider + public interface ICycleEventsProvider : IDisposable { /// /// Configuration object holding the duration of the different phases in the cycle. @@ -47,6 +48,12 @@ public interface ICycleEventsProvider /// A TimeSpan representing the time to wait until next delta production cycle starts. TimeSpan GetTimeSpanUntilNextCycleStart(); + /// + /// Starts the event cycle provider. + /// + /// A Task represending the corresponding async method. + Task StartAsync(); + /// /// Terminate the emission of state changes events on the stream /// diff --git a/src/Catalyst.Abstractions/Consensus/Cycle/ICycleSchedulerProvider.cs b/src/Catalyst.Abstractions/Consensus/Cycle/ICycleSchedulerProvider.cs index d157e88571..28281dd1f8 100644 --- a/src/Catalyst.Abstractions/Consensus/Cycle/ICycleSchedulerProvider.cs +++ b/src/Catalyst.Abstractions/Consensus/Cycle/ICycleSchedulerProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Consensus/Cycle/IPhase.cs b/src/Catalyst.Abstractions/Consensus/Cycle/IPhase.cs index 1c0dd5c128..34f9c2e522 100644 --- a/src/Catalyst.Abstractions/Consensus/Cycle/IPhase.cs +++ b/src/Catalyst.Abstractions/Consensus/Cycle/IPhase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,7 @@ #endregion using System; -using LibP2P; +using Lib.P2P; namespace Catalyst.Abstractions.Consensus.Cycle { diff --git a/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseName.cs b/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseName.cs index 625ebec329..d89f8c5c92 100644 --- a/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseName.cs +++ b/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseName.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseStatus.cs b/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseStatus.cs index d3a828dbf7..5936c8bfe8 100644 --- a/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseStatus.cs +++ b/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseStatus.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseTimings.cs b/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseTimings.cs index 36e7b49160..7b17fd4a13 100644 --- a/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseTimings.cs +++ b/src/Catalyst.Abstractions/Consensus/Cycle/IPhaseTimings.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaBuilder.cs b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaBuilder.cs index 6616ea66cc..86086e278f 100644 --- a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaBuilder.cs +++ b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaBuilder.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,7 @@ #endregion using Catalyst.Protocol.Wire; -using LibP2P; +using Lib.P2P; namespace Catalyst.Abstractions.Consensus.Deltas { diff --git a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaCache.cs b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaCache.cs index 6c47fa7ab4..c106573a31 100644 --- a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaCache.cs +++ b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaCache.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,7 +24,7 @@ using System.Threading; using Catalyst.Protocol.Deltas; using Catalyst.Protocol.Wire; -using LibP2P; +using Lib.P2P; namespace Catalyst.Abstractions.Consensus.Deltas { @@ -65,6 +65,8 @@ bool TryGetOrAddConfirmedDelta(Cid cid, /// The candidate produced locally (>) /// The full content of the produced delta. void AddLocalDelta(CandidateDeltaBroadcast localCandidate, Delta delta); + + void AddLocalDelta(Cid cid, Delta delta); /// /// Dfs address of the content for the very first delta. diff --git a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaCacheChangeTokenProvider.cs b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaCacheChangeTokenProvider.cs index 65d799a958..6ed5c47d93 100644 --- a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaCacheChangeTokenProvider.cs +++ b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaCacheChangeTokenProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaDfsReader.cs b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaDfsReader.cs index 304542af2e..c80b95f606 100644 --- a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaDfsReader.cs +++ b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaDfsReader.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,7 @@ using System.Threading; using Catalyst.Protocol.Deltas; -using LibP2P; +using Lib.P2P; namespace Catalyst.Abstractions.Consensus.Deltas { diff --git a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaElector.cs b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaElector.cs index a1dc19774c..b154b6f163 100644 --- a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaElector.cs +++ b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaElector.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,7 @@ using System; using Catalyst.Protocol.Wire; -using LibP2P; +using Lib.P2P; namespace Catalyst.Abstractions.Consensus.Deltas { diff --git a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaHashProvider.cs b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaHashProvider.cs index 3a990ebd12..718e52ec92 100644 --- a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaHashProvider.cs +++ b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaHashProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,8 +22,7 @@ #endregion using System; -using Catalyst.Abstractions.Dfs; -using LibP2P; +using Lib.P2P; namespace Catalyst.Abstractions.Consensus.Deltas { @@ -62,7 +61,9 @@ public interface IDeltaHashProvider /// /// /// The hash, or address on the DFS, of the latest ledger state update as a string, as returned in - /// + /// + /// IDfsService.AddAsync + /// /// Cid GetLatestDeltaHash(DateTime? asOf = null); diff --git a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaHub.cs b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaHub.cs index f02be3c937..f36f0af721 100644 --- a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaHub.cs +++ b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaHub.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,7 +25,7 @@ using System.Threading.Tasks; using Catalyst.Protocol.Deltas; using Catalyst.Protocol.Wire; -using LibP2P; +using Lib.P2P; namespace Catalyst.Abstractions.Consensus.Deltas { diff --git a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaTransactionRetriever.cs b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaTransactionRetriever.cs index 14557edde7..c9c0a08f2e 100644 --- a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaTransactionRetriever.cs +++ b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaTransactionRetriever.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,7 @@ #endregion using System.Collections.Generic; -using Catalyst.Protocol.Wire; +using Catalyst.Protocol.Transaction; namespace Catalyst.Abstractions.Consensus.Deltas { @@ -47,6 +47,6 @@ public interface IDeltaTransactionRetriever /// /// The maximum number of transactions to be returned by the method. /// The top transactions in order of - IList GetMempoolTransactionsByPriority(int maxCount = int.MaxValue); + IList GetMempoolTransactionsByPriority(int maxCount = int.MaxValue); } } diff --git a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaVoter.cs b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaVoter.cs index eea6119cc9..36660338fb 100644 --- a/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaVoter.cs +++ b/src/Catalyst.Abstractions/Consensus/Deltas/IDeltaVoter.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,7 @@ using System; using Catalyst.Protocol.Wire; -using LibP2P; +using Lib.P2P; namespace Catalyst.Abstractions.Consensus.Deltas { diff --git a/src/Catalyst.Abstractions/Consensus/Deltas/IScoredCandidateDelta.cs b/src/Catalyst.Abstractions/Consensus/Deltas/IScoredCandidateDelta.cs index 79c6350232..003cdeb417 100644 --- a/src/Catalyst.Abstractions/Consensus/Deltas/IScoredCandidateDelta.cs +++ b/src/Catalyst.Abstractions/Consensus/Deltas/IScoredCandidateDelta.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Consensus/IConsensus.cs b/src/Catalyst.Abstractions/Consensus/IConsensus.cs index fb96a98d40..5a0c0eb84c 100644 --- a/src/Catalyst.Abstractions/Consensus/IConsensus.cs +++ b/src/Catalyst.Abstractions/Consensus/IConsensus.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Consensus/IDateTimeProvider.cs b/src/Catalyst.Abstractions/Consensus/IDateTimeProvider.cs index 4efc1e0ca1..3a3ba60fe6 100644 --- a/src/Catalyst.Abstractions/Consensus/IDateTimeProvider.cs +++ b/src/Catalyst.Abstractions/Consensus/IDateTimeProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Consensus/ITransactionComparer.cs b/src/Catalyst.Abstractions/Consensus/ITransactionComparer.cs index 2b47f6bfc8..76e602e8a8 100644 --- a/src/Catalyst.Abstractions/Consensus/ITransactionComparer.cs +++ b/src/Catalyst.Abstractions/Consensus/ITransactionComparer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,9 +22,9 @@ #endregion using System.Collections.Generic; -using Catalyst.Protocol.Wire; +using Catalyst.Protocol.Transaction; namespace Catalyst.Abstractions.Consensus { - public interface ITransactionComparer : IComparer { } + public interface ITransactionComparer : IComparer { } } diff --git a/src/Catalyst.Abstractions/Contract/ICallableContractProxy.cs b/src/Catalyst.Abstractions/Contract/ICallableContractProxy.cs new file mode 100644 index 0000000000..47ed712854 --- /dev/null +++ b/src/Catalyst.Abstractions/Contract/ICallableContractProxy.cs @@ -0,0 +1,32 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Nethermind.Core; + +namespace Catalyst.Abstractions.Contract +{ + public interface ICallableContractProxy + { + byte[] Call(Address contractAddress, byte[] data); + } +} diff --git a/src/Catalyst.Abstractions/Contract/IContract.cs b/src/Catalyst.Abstractions/Contract/IContract.cs index 68c021ffd1..44e955c22b 100644 --- a/src/Catalyst.Abstractions/Contract/IContract.cs +++ b/src/Catalyst.Abstractions/Contract/IContract.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Contract/IValidatorSetContract.cs b/src/Catalyst.Abstractions/Contract/IValidatorSetContract.cs new file mode 100644 index 0000000000..ea5911dd14 --- /dev/null +++ b/src/Catalyst.Abstractions/Contract/IValidatorSetContract.cs @@ -0,0 +1,32 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Nethermind.Core; + +namespace Catalyst.Abstractions.Contract +{ + public interface IValidatorSetContract + { + Address[] GetValidators(Address contractAddress); + } +} diff --git a/src/Catalyst.Abstractions/Cryptography/CryptoExtensions.cs b/src/Catalyst.Abstractions/Cryptography/CryptoExtensions.cs new file mode 100644 index 0000000000..d12bfbaba9 --- /dev/null +++ b/src/Catalyst.Abstractions/Cryptography/CryptoExtensions.cs @@ -0,0 +1,89 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Buffers; +using System.IO; +using Google.Protobuf; +using MultiFormats; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using ProtoBuf; + +namespace Catalyst.Abstractions.Cryptography +{ + public static class CryptoExtensions + { + /// + /// Signs message using provided private key and returns the signature. + /// + /// + /// + /// + /// + /// + public static ISignature Sign(this ICryptoContext crypto, IPrivateKey privateKey, IMessage message, IMessage context) + { + ProtoPreconditions.CheckNotNull(message, nameof(message)); + ProtoPreconditions.CheckNotNull(context, nameof(context)); + + var messageSize = message.CalculateSize(); + var contextSize = context.CalculateSize(); + var array = ArrayPool.Shared.Rent(messageSize + contextSize); + + using (var output = new CodedOutputStream(array)) + { + message.WriteTo(output); + context.WriteTo(output); + } + + var signature = crypto.Sign(privateKey, array.AsSpan(0, messageSize), array.AsSpan(messageSize, contextSize)); + + ArrayPool.Shared.Return(array); + + return signature; + } + + public static bool Verify(this ICryptoContext crypto, ISignature signature, IMessage message, IMessage context) + { + ProtoPreconditions.CheckNotNull(message, nameof(message)); + ProtoPreconditions.CheckNotNull(context, nameof(context)); + + var messageSize = message.CalculateSize(); + var contextSize = context.CalculateSize(); + var array = ArrayPool.Shared.Rent(messageSize + contextSize); + + using (var output = new CodedOutputStream(array)) + { + message.WriteTo(output); + context.WriteTo(output); + } + + var result = crypto.Verify(signature, array.AsSpan(0, messageSize), array.AsSpan(messageSize, contextSize)); + + ArrayPool.Shared.Return(array); + + return result; + } + } +} diff --git a/src/Catalyst.Abstractions/Cryptography/ICertificateStore.cs b/src/Catalyst.Abstractions/Cryptography/ICertificateStore.cs index 6979594348..eaf16d01da 100644 --- a/src/Catalyst.Abstractions/Cryptography/ICertificateStore.cs +++ b/src/Catalyst.Abstractions/Cryptography/ICertificateStore.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cryptography/ICryptoContext.cs b/src/Catalyst.Abstractions/Cryptography/ICryptoContext.cs index d8da713ccf..816665feee 100644 --- a/src/Catalyst.Abstractions/Cryptography/ICryptoContext.cs +++ b/src/Catalyst.Abstractions/Cryptography/ICryptoContext.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,6 +21,9 @@ #endregion +using System; +using System.Collections.Generic; + namespace Catalyst.Abstractions.Cryptography { public interface ICryptoContext @@ -101,8 +104,10 @@ public interface ICryptoContext /// /// /// - ISignature Sign(IPrivateKey privateKey, byte[] message, byte[] context); + ISignature Sign(IPrivateKey privateKey, ReadOnlySpan message, ReadOnlySpan context); + + bool Verify(ISignature signature, ReadOnlySpan message, ReadOnlySpan context); - bool Verify(ISignature signature, byte[] message, byte[] context); + bool BatchVerify(IList signatures, IList messages, ReadOnlySpan context); } } diff --git a/src/Catalyst.Abstractions/Cryptography/IDeterministicRandom.cs b/src/Catalyst.Abstractions/Cryptography/IDeterministicRandom.cs index 9d3012dc92..8169517017 100644 --- a/src/Catalyst.Abstractions/Cryptography/IDeterministicRandom.cs +++ b/src/Catalyst.Abstractions/Cryptography/IDeterministicRandom.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cryptography/IKey.cs b/src/Catalyst.Abstractions/Cryptography/IKey.cs new file mode 100644 index 0000000000..25cb9a1bd8 --- /dev/null +++ b/src/Catalyst.Abstractions/Cryptography/IKey.cs @@ -0,0 +1,50 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using MultiFormats; + +namespace Catalyst.Abstractions.Cryptography +{ + /// + /// Information about a cryptographic key. + /// + public interface IKey + { + /// + /// Unique identifier. + /// + /// + /// The of the key's public key. + /// + MultiHash Id { get; } + + /// + /// The locally assigned name to the key. + /// + /// + /// The name is only unique within the local peer node. The + /// is universally unique. + /// + string Name { get; } + } +} diff --git a/src/Catalyst.Abstractions/Cryptography/IPasswordManager.cs b/src/Catalyst.Abstractions/Cryptography/IPasswordManager.cs index c3f856fb84..c9aca76af4 100644 --- a/src/Catalyst.Abstractions/Cryptography/IPasswordManager.cs +++ b/src/Catalyst.Abstractions/Cryptography/IPasswordManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -31,6 +31,20 @@ namespace Catalyst.Abstractions.Cryptography /// public interface IPasswordManager { + /// + /// Prompt the user for a password. + /// + /// The type of password. + /// A message providing some context to the user, + /// for instance which password is being requested. + /// The password read and returned as a SecureString + /// Once the password has been use, it is recommended to dispose of it. + /// + /// https://docs.microsoft.com/en-us/dotnet/api/system.security.securestring?view=netcore-2.2 + /// + /// + SecureString PromptPassword(PasswordRegistryTypes passwordType, string promptMessage = null); + /// /// Try to retrieve a password from the registry, if not found, prompt the /// user for it. diff --git a/src/Catalyst.Abstractions/Cryptography/IPasswordReader.cs b/src/Catalyst.Abstractions/Cryptography/IPasswordReader.cs index d2f68e814a..6adae1d09f 100644 --- a/src/Catalyst.Abstractions/Cryptography/IPasswordReader.cs +++ b/src/Catalyst.Abstractions/Cryptography/IPasswordReader.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cryptography/IPasswordRegistry.cs b/src/Catalyst.Abstractions/Cryptography/IPasswordRegistry.cs index 693ea0a70b..6209173444 100644 --- a/src/Catalyst.Abstractions/Cryptography/IPasswordRegistry.cs +++ b/src/Catalyst.Abstractions/Cryptography/IPasswordRegistry.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cryptography/IPasswordRepeater.cs b/src/Catalyst.Abstractions/Cryptography/IPasswordRepeater.cs new file mode 100644 index 0000000000..425688a748 --- /dev/null +++ b/src/Catalyst.Abstractions/Cryptography/IPasswordRepeater.cs @@ -0,0 +1,42 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Security; +using System.Threading.Tasks; + +namespace Catalyst.Abstractions.Cryptography +{ + public interface IPasswordRepeater + { + /// + /// Prompt and return the secure password. + /// + /// A secure string containing the correct password. + Task PromptAndReceiveAsync(); + + /// + /// Prompt and add the secure password to the password registry. + /// + Task PromptAndAddPasswordToRegistryAsync(); + } +} diff --git a/src/Catalyst.Abstractions/Cryptography/IPrivateKey.cs b/src/Catalyst.Abstractions/Cryptography/IPrivateKey.cs index 9d1ad0c70f..d8ba2c93c6 100644 --- a/src/Catalyst.Abstractions/Cryptography/IPrivateKey.cs +++ b/src/Catalyst.Abstractions/Cryptography/IPrivateKey.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cryptography/IPublicKey.cs b/src/Catalyst.Abstractions/Cryptography/IPublicKey.cs index 54f2c0d7f7..cc0b27d65e 100644 --- a/src/Catalyst.Abstractions/Cryptography/IPublicKey.cs +++ b/src/Catalyst.Abstractions/Cryptography/IPublicKey.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Cryptography/ISignature.cs b/src/Catalyst.Abstractions/Cryptography/ISignature.cs index b99b720500..f3eb56c496 100644 --- a/src/Catalyst.Abstractions/Cryptography/ISignature.cs +++ b/src/Catalyst.Abstractions/Cryptography/ISignature.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/DAO/IMapperInitializer.cs b/src/Catalyst.Abstractions/DAO/IMapperInitializer.cs index a8da9de446..4483d1fc5b 100644 --- a/src/Catalyst.Abstractions/DAO/IMapperInitializer.cs +++ b/src/Catalyst.Abstractions/DAO/IMapperInitializer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Dfs/BlockExchange/CidEventArgs.cs b/src/Catalyst.Abstractions/Dfs/BlockExchange/CidEventArgs.cs new file mode 100644 index 0000000000..06edbc66c6 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/BlockExchange/CidEventArgs.cs @@ -0,0 +1,46 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Lib.P2P; + +namespace Catalyst.Abstractions.Dfs.BlockExchange +{ + /// + /// The content addressable ID related to an event. + /// + /// + /// + /// Catalyst.Core.Modules.Dfs.BlockExchange.BitswapService.BlockNeeded + /// + public sealed class CidEventArgs : EventArgs + { + /// + /// The content addressable ID. + /// + /// + /// The unique ID of the block. + /// + public Cid Id { get; set; } + } +} diff --git a/src/Catalyst.Abstractions/Dfs/BlockExchange/IBitswapService.cs b/src/Catalyst.Abstractions/Dfs/BlockExchange/IBitswapService.cs new file mode 100644 index 0000000000..0f63f155c6 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/BlockExchange/IBitswapService.cs @@ -0,0 +1,217 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.BlockExchange.Protocols; +using Catalyst.Abstractions.Dfs.CoreApi; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Abstractions.Dfs.BlockExchange +{ + public interface IBitswapService : IService + { + /// + /// The supported bitswap protocols. + /// + /// + /// Defaults to and . + /// + IBitswapProtocol[] Protocols { get; set; } + + /// + /// Provides access to other peers. + /// + ISwarmService SwarmService { get; set; } + + /// + /// Provides access to blocks of data. + /// + IBlockApi BlockService { get; set; } + + /// + /// Statistics on the bitswap component. + /// + /// + BitswapData Statistics { get; } + + /// + /// Gets the bitswap ledger for the specified peer. + /// + /// + /// The peer to get information on. If the peer is unknown, then a ledger + /// with zeros is returned. + /// + /// + /// Statistics on the bitswap blocks exchanged with the peer. + /// + /// + BitswapLedger PeerLedger(Peer peer); + + /// + /// Raised when a blocked is needed. + /// + /// + /// Only raised when a block is first requested. + /// + event EventHandler BlockNeeded; + + /// + /// The blocks needed by the peer. + /// + /// + /// The unique ID of the peer. + /// + /// + /// The sequence of CIDs need by the . + /// + IEnumerable PeerWants(MultiHash peer); + + /// + /// Adds a block to the want list. + /// + /// + /// The CID of the block to add to the want list. + /// + /// + /// The unique ID of the peer that wants the block. This is for + /// information purposes only. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the contents of block. + /// + /// + /// Other peers are informed that the block is needed by this peer. Hopefully, + /// someone will forward it to us. + /// + /// Besides using for cancellation, the + /// method will also cancel the operation. + /// + /// + Task WantAsync(Cid id, MultiHash peer, CancellationToken cancel); + + /// + /// Removes the block from the want list. + /// + /// + /// The CID of the block to remove from the want list. + /// + /// + /// Any tasks waiting for the block are cancelled. + /// + /// No exception is thrown if the is not + /// on the want list. + /// + /// + void Unwant(Cid id); + + /// + /// Indicate that a remote peer sent a block. + /// + /// + /// The peer that sent the block. + /// + /// + /// The data for the block. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// + /// Updates the statistics. + /// + /// + /// If the block is acceptable then the is added to local cache + /// via the . + /// + /// + Task OnBlockReceivedAsync(Peer remote, byte[] block); + + /// + /// Indicate that a remote peer sent a block. + /// + /// + /// The peer that sent the block. + /// + /// + /// The data for the block. + /// + /// + /// The of the block. + /// + /// + /// The multihash algorithm name of the block. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// + /// Updates the statistics. + /// + /// + /// If the block is acceptable then the is added to local cache + /// via the . + /// + /// + Task OnBlockReceivedAsync(Peer remote, byte[] block, string contentType, string multiHash); + + /// + /// Indicate that the local peer sent a block to a remote peer. + /// + /// + /// The peer that sent the block. + /// + /// + /// The data for the block. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task OnBlockSentAsync(Peer remote, IDataBlock block); + + /// + /// Indicate that a block is found. + /// + /// + /// The block that was found. + /// + /// + /// The number of consumers waiting for the . + /// + /// + /// Found should be called whenever a new block is discovered. + /// It will continue any Task that is waiting for the block and + /// remove the block from the want list. + /// + int Found(IDataBlock block); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/BlockExchange/Protocols/IBitswapProtocol.cs b/src/Catalyst.Abstractions/Dfs/BlockExchange/Protocols/IBitswapProtocol.cs new file mode 100644 index 0000000000..f38ab055e5 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/BlockExchange/Protocols/IBitswapProtocol.cs @@ -0,0 +1,60 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Protocols; + +namespace Catalyst.Abstractions.Dfs.BlockExchange.Protocols +{ + /// + /// Features of a bitswap protocol. + /// + public interface IBitswapProtocol : IPeerProtocol + { + /// + /// Send a want list. + /// + /// + /// The destination of the want list. + /// + /// + /// A sequence of . + /// + /// + /// true if is the full want list. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task SendWantsAsync(Stream stream, + IEnumerable wants, + bool full = true, + CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/BlockExchange/WantedBlock.cs b/src/Catalyst.Abstractions/Dfs/BlockExchange/WantedBlock.cs new file mode 100644 index 0000000000..a980c834f4 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/BlockExchange/WantedBlock.cs @@ -0,0 +1,51 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Threading.Tasks; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Abstractions.Dfs.BlockExchange +{ + /// + /// A content addressable block that is wanted by a peer. + /// + public class WantedBlock + { + /// + /// The content ID of the block; + /// + public Cid Id { set; get; } + + /// + /// The peers that want the block. + /// + public List Peers { set; get; } + + /// + /// The consumers that are waiting for the block. + /// + public List> Consumers { set; get; } + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/BitswapData.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/BitswapData.cs new file mode 100644 index 0000000000..f8b4eac659 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/BitswapData.cs @@ -0,0 +1,88 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// The statistics for . + /// + public class BitswapData + { + /// + /// TODO: Unknown. + /// + public int ProvideBufLen; + + /// + /// The content that is wanted. + /// + public IEnumerable Wantlist; + + /// + /// The known peers. + /// + public IEnumerable Peers; + + /// + /// The number of blocks sent by other peers. + /// + public ulong BlocksReceived; + + /// + /// The number of bytes sent by other peers. + /// + public ulong DataReceived; + + /// + /// The number of blocks sent to other peers. + /// + public ulong BlocksSent; + + /// + /// The number of bytes sent to other peers. + /// + public ulong DataSent; + + /// + /// The number of duplicate blocks sent by other peers. + /// + /// + /// A duplicate block is a block that is already stored in the + /// local repository. + /// + public ulong DupBlksReceived; + + /// + /// The number of duplicate bytes sent by other peers. + /// + /// + /// A duplicate block is a block that is already stored in the + /// local repository. + /// + public ulong DupDataReceived; + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/BitswapLedger.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/BitswapLedger.cs new file mode 100644 index 0000000000..2200a42e14 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/BitswapLedger.cs @@ -0,0 +1,90 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Lib.P2P; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Statistics on the bitswap blocks exchanged with another . + /// + /// + public class BitswapLedger + { + /// + /// The that pertains to this ledger. + /// + /// + /// The peer that is being monitored. + /// + public Peer Peer { get; set; } + + /// + /// The number of blocks exchanged with the . + /// + /// + /// The number of blocks sent by the peer or sent by us to the peer. + /// + public ulong BlocksExchanged { get; set; } + + /// + /// The number of bytes sent by the to us. + /// + /// + /// The number of bytes. + /// + public ulong DataReceived { get; set; } + + /// + /// The number of bytes sent by us to the + /// + /// + /// The number of bytes. + /// + public ulong DataSent { get; set; } + + /// + /// The calculated debt to the peer. + /// + /// + /// divided by . + /// A value less than 1 indicates that we are in debt to the + /// . + /// + public float DebtRatio + { + get + { + return DataSent / (float) (DataReceived + 1); // +1 is to prevent division by zero + } + } + + /// + /// Determines if we owe the some blocks. + /// + /// + /// true if we owe data to the peer; otherwise, false. + /// + public bool IsInDebt => DebtRatio < 1; + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IBitSwapApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IBitSwapApi.cs new file mode 100644 index 0000000000..b090e0ed7d --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IBitSwapApi.cs @@ -0,0 +1,126 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Data trading module for IPFS. Its purpose is to request blocks from and + /// send blocks to other peers in the network. + /// + /// + /// BitSwap has two primary jobs + /// + /// + /// + /// Attempt to acquire blocks from the network that have been requested by the client + /// + /// + /// + /// + /// Judiciously (though strategically) send blocks in its possession to other peers who want them + /// + /// + /// + /// + /// Bitswap spec + public interface IBitSwapApi + { + /// + /// Gets a block from the IPFS network. + /// + /// + /// The of the block. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous get operation. The task's value + /// contains the block's id and data. + /// + /// + /// Waits for another peer to supply the block with the . + /// + Task GetAsync(Cid id, CancellationToken cancel = default); + + /// + /// The blocks that are needed by a peer. + /// + /// + /// The id of an IPFS peer. If not specified (e.g. null), then the local + /// peer is used. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// contains the sequence of blocks needed by the . + /// + Task> WantsAsync(MultiHash peer = null, CancellationToken cancel = default); + + /// + /// Remove the CID from the want list. + /// + /// + /// The content that is no longer needed. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// Any outstanding for the + /// are cancelled. + /// + void UnWant(Cid id, CancellationToken cancel = default); + + /// + /// Gets information on the blocks exchanged with a specific . + /// + /// + /// The peer to get information on. If the peer is unknown, then a ledger + /// with zeros is returned. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// contains the for the . + /// + BitswapLedger GetBitSwapLedger(Peer peer, CancellationToken cancel = default); + + int FoundBlock(IDataBlock block); + + BitswapData GetBitSwapStatistics(); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IBlockApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IBlockApi.cs new file mode 100644 index 0000000000..0d79d535bd --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IBlockApi.cs @@ -0,0 +1,174 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Manages IPFS blocks. + /// + /// + /// An IPFS Block is a byte sequence that represents an IPFS Object + /// (i.e. serialized byte buffers). It is useful to talk about them as + /// "blocks" in Bitswap + /// and other things that do not care about what is being stored. + /// + /// + /// Block API spec + public interface IBlockApi + { + IPinApi PinApi { get; set; } + + /// + /// Gets an IPFS block. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// The of the block. + /// + /// + /// A task that represents the asynchronous get operation. The task's value + /// contains the block's id and data. + /// + Task GetAsync(Cid id, CancellationToken cancel = default); + + /// + /// Stores a byte array as an IPFS block. + /// + /// + /// The byte array to send to the IPFS network. + /// + /// + /// The content type or format of the ; such as "raw" or "dag-db". + /// See for more details. + /// + /// + /// The algorithm name used to produce the . + /// + /// + /// The algorithm name used to produce the . + /// + /// + /// If true the block is pinned to local storage and will not be + /// garbage collected. The default is false. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous put operation. The task's value is + /// the block's . + /// + Task PutAsync(byte[] data, + string contentType = Cid.DefaultContentType, + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = false, + CancellationToken cancel = default); + + /// + /// Stores a stream as an IPFS block. + /// + /// + /// The of data to send to the IPFS network. + /// + /// + /// The content type or format of the ; such as "raw" or "dag-db". + /// See for more details. + /// + /// + /// The algorithm name used to produce the . + /// + /// + /// The algorithm name used to produce the . + /// + /// + /// If true the block is pinned to local storage and will not be + /// garbage collected. The default is false. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous put operation. The task's value is + /// the block's . + /// + Task PutAsync(Stream data, + string contentType = Cid.DefaultContentType, + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = false, + CancellationToken cancel = default); + + /// + /// Information on an IPFS block. + /// + /// + /// The of the block. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// contains the block's id and size or null. + /// + /// + /// Only the local repository is consulted for the block. If + /// does not exist, then null is retuned. + /// + Task StatAsync(Cid id, CancellationToken cancel = default); + + /// + /// Remove an IPFS block. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// The of the block. + /// + /// + /// If true do not raise exception when does not + /// exist. Default value is false. + /// + /// + /// The awaited Task will return the deleted or null + /// if the does not exist and + /// is true. + /// + /// + /// This removes the block from the local cache and does not affect other peers. + /// + Task RemoveAsync(Cid id, + bool ignoreNonexistent = false, + CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IBlockRepository.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IBlockRepository.cs new file mode 100644 index 0000000000..d9af61673c --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IBlockRepository.cs @@ -0,0 +1,84 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading; +using System.Threading.Tasks; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Manages all the blocks stored locally. + /// + /// + public interface IBlockRepositoryApi + { + /// + /// Perform a garbage collection sweep on the repo. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// TODO: not sure what this should return. + /// + Task RemoveGarbageAsync(CancellationToken cancel = default); + + /// + /// Get statistics on the repository. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the current . + /// + /// + /// Same as . + /// + Task StatisticsAsync(CancellationToken cancel = default); + + /// + /// Verify all blocks in repo are not corrupted. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// TODO: not sure what this should return. + /// + Task VerifyAsync(CancellationToken cancel = default); + + /// + /// Gets the version number of the repo. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the version number of the data block repository. + /// + Task VersionAsync(CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IBootstrapApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IBootstrapApi.cs new file mode 100644 index 0000000000..160f914c87 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IBootstrapApi.cs @@ -0,0 +1,112 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using MultiFormats; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Manages the list of initial peers. + /// + /// + /// The API manipulates the "bootstrap list", which contains + /// the addresses of the bootstrap nodes. These are the trusted peers from + /// which to learn about other peers in the network. + /// + /// + /// Bootstrap API spec + public interface IBootstrapApi + { + /// + /// Adds a new peer. + /// + /// + /// The address must end with the ipfs protocol and the public ID + /// of the peer. For example "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the address that was added or null if the address is already + /// in the bootstrap list. + /// + Task AddAsync(MultiAddress address, CancellationToken cancel = default); + + /// + /// Adds the default peers to the list. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the sequence of addresses that were added. + /// + Task> AddDefaultsAsync(CancellationToken cancel = default); + + /// + /// List all the peers. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a sequence of addresses. + /// + Task> ListAsync(CancellationToken cancel = default); + + /// + /// Delete the specified peer. + /// + /// + /// The address must end with the ipfs protocol and the public ID + /// of the peer. For example "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the address that was removed or null if the + /// is not in the bootstrap list. + /// + Task RemoveAsync(MultiAddress address, CancellationToken cancel = default); + + /// + /// Remove all the peers. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task RemoveAllAsync(CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IConfigApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IConfigApi.cs new file mode 100644 index 0000000000..9fd6d35bd9 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IConfigApi.cs @@ -0,0 +1,105 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json.Linq; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Manages the IPFS Configuration. + /// + /// + /// + /// Configuration values are JSON. Json.NET + /// is used to represent JSON. + /// + /// + /// Config API spec + public interface IConfigApi + { + /// + /// Gets the entire configuration. + /// + /// + /// A containing the configuration. + /// + Task GetAsync(CancellationToken cancel = default); + + /// + /// Gets the value of a configuration key. + /// + /// + /// The key name, such as "Addresses.API". + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// The value of the as . + /// + /// + /// When the does not exist. + /// + /// + /// Keys are case sensistive. + /// + Task GetAsync(string key, CancellationToken cancel = default); + + /// + /// Adds or replaces a configuration value. + /// + /// + /// The key name, such as "Addresses.API". + /// + /// + /// The new value of the . + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + Task SetAsync(string key, string value, CancellationToken cancel = default); + + /// + /// Adds or replaces a configuration value. + /// + /// + /// The key name, such as "Addresses.API". + /// + /// + /// The new JSON value of the . + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + Task SetAsync(string key, JToken value, CancellationToken cancel = default); + + /// + /// Replaces the entire configuration. + /// + /// + Task ReplaceAsync(JObject config); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/ICoreApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/ICoreApi.cs new file mode 100644 index 0000000000..cb499c8d79 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/ICoreApi.cs @@ -0,0 +1,165 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Keystore; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// The IPFS Core API. + /// + /// + /// The Core API defines a set of interfaces to manage IPFS. + /// + /// + public interface ICoreApi + { + /// + /// Provides access to the Bitswap API. + /// + /// + /// An object that implements . + /// + IBitSwapApi BitSwapApi { get; } + + /// + /// Provides access to the Block API. + /// + /// + /// An object that implements . + /// + IBlockApi BlockApi { get; } + + /// + /// Provides access to the Block Repository API. + /// + /// + /// An object that implements . + /// + IBlockRepositoryApi BlockRepositoryApi { get; } + + /// + /// Provides access to the Bootstrap API. + /// + /// + /// An object that implements . + /// + IBootstrapApi BootstrapApi { get; } + + /// + /// Provides access to the Config API. + /// + /// + /// An object that implements . + /// + IConfigApi ConfigApi { get; } + + /// + /// Provides access to the Dag API. + /// + /// + /// An object that implements . + /// + IDagApi DagApi { get; } + + /// + /// Provides access to the DHT API. + /// + /// + /// An object that implements . + /// + IDhtApi DhtApi { get; } + + /// + /// Provides access to the DNS API. + /// + /// + /// An object that implements . + /// + IDnsApi DnsApi { get; } + + /// + /// Provides access to the File System API. + /// + /// + /// An object that implements . + /// + IUnixFsApi UnixFsApi { get; } + + /// + /// Provides access to the Key API. + /// + /// + /// An object that implements . + /// + IKeyApi KeyApi { get; } + + /// + /// Provides access to the Name API. + /// + /// + /// An object that implements . + /// + INameApi NameApi { get; } + + /// + /// Provides access to the Object API. + /// + /// + /// An object that implements . + /// + IObjectApi ObjectApi { get; } + + /// + /// Provides access to the Pin API. + /// + /// + /// An object that implements . + /// + IPinApi PinApi { get; } + + /// + /// Provides access to the PubSub API. + /// + /// + /// An object that implements . + /// + IPubSubApi PubSubApi { get; } + + /// + /// Provides access to the Stats (statistics) API. + /// + /// + /// An object that implements . + /// + IStatsApi StatsApi { get; } + + /// + /// Provides access to the Swarm API. + /// + /// + /// An object that implements . + /// + ISwarmApi SwarmApi { get; } + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IDagApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IDagApi.cs new file mode 100644 index 0000000000..88258c4449 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IDagApi.cs @@ -0,0 +1,196 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P; +using MultiFormats; +using Newtonsoft.Json.Linq; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Manages the IPLD (linked data) Directed Acrylic Graph. + /// + /// + /// The DAG API is a replacement of the , which only supported 'dag-pb'. + /// This API supports other IPLD formats, such as cbor, ethereum-block, git, ... + /// + /// + /// + /// Dag API spec + public interface IDagApi + { + /// + /// Put JSON data as an IPLD node. + /// + /// + /// The JSON data to send to the network. + /// + /// + /// The content type or format of the ; such as "dag-pb" or "dag-cbor". + /// See for more details. Defaults to "dag-cbor". + /// + /// + /// The algorithm name used to produce the . + /// + /// + /// The algorithm name used to produce the . + /// + /// + /// If true the is pinned to local storage and will not be + /// garbage collected. The default is true. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous put operation. The task's value is + /// the data's . + /// + Task PutAsync(JObject data, + string contentType = "dag-cbor", + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = true, + CancellationToken cancel = default); + + /// + /// Put a stream of JSON as an IPLD node. + /// + /// + /// The stream of JSON. + /// + /// + /// The content type or format of the ; such as "dag-pb" or "dag-cbor". + /// See for more details. Defaults to "dag-cbor". + /// + /// + /// The algorithm name used to produce the . + /// + /// + /// The algorithm name used to produce the . + /// + /// + /// If true the is pinned to local storage and will not be + /// garbage collected. The default is true. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous put operation. The task's value is + /// the data's . + /// + Task PutAsync(Stream data, + string contentType = "dag-cbor", + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = true, + CancellationToken cancel = default); + + /// + /// Put an object as an IPLD node. + /// + /// + /// The object to add. + /// + /// + /// The content type or format of the ; such as "dag-pb" or "dag-cbor". + /// See for more details. Defaults to "dag-cbor". + /// + /// + /// The algorithm name used to produce the . + /// + /// + /// The algorithm name used to produce the . + /// + /// + /// If true the is pinned to local storage and will not be + /// garbage collected. The default is true. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous put operation. The task's value is + /// the data's . + /// + Task PutAsync(Object data, + string contentType = "dag-cbor", + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = true, + CancellationToken cancel = default); + + /// + /// Get an IPLD node. + /// + /// + /// The of the IPLD node. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous get operation. The task's value + /// contains the node's content as JSON. + /// + Task GetAsync(Cid id, CancellationToken cancel = default); + + /// + /// Gets the content of an IPLD node. + /// + /// + /// A path, such as "cid", "/ipfs/cid/" or "cid/a". + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous get operation. The task's value + /// contains the path's value. + /// + Task GetAsync(string path, CancellationToken cancel = default); + + /// + /// Get an IPLD node of the specific type. + /// + /// + /// The object's type. + /// + /// + /// The of the IPLD node. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous get operation. The task's value + /// is a new instance of the class. + /// + Task GetAsync(Cid id, CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IDhtApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IDhtApi.cs new file mode 100644 index 0000000000..3947d147b1 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IDhtApi.cs @@ -0,0 +1,40 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Lib.P2P.Routing; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Manages the Distributed Hash Table. + /// + /// + /// The DHT is a place to store, not the value, but pointers to peers who have + /// the actual value. + /// + /// See the ongoing DHT specification at + /// + /// + /// DHT API spec + public interface IDhtApi : IPeerRouting, IContentRouting, IValueStore { } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IDnsApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IDnsApi.cs new file mode 100644 index 0000000000..a5a511de9e --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IDnsApi.cs @@ -0,0 +1,77 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading; +using System.Threading.Tasks; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// DNS mapping to IPFS. + /// + /// + /// Multihashes are hard to remember, but domain names are usually easy to + /// remember. To create memorable aliases for multihashes, DNS TXT + /// records can point to other DNS links, IPFS objects, IPNS keys, etc. + /// + public interface IDnsApi + { + /// + /// Resolve a domain name to an IPFS path. + /// + /// + /// An domain name, such as "ipfs.io". + /// + /// + /// Resolve until the result is not a DNS link. Defaults to false. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the resolved IPFS path as a , such as + /// /ipfs/QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao. + /// + /// + /// A DNS TXT record with a "dnslink=..." entry is expected to exist. The + /// value of the "dnslink" is an IPFS path to another IPFS object. + /// + /// A DNS query is generated for both and + /// _dnslink.. + /// + /// + /// + /// ResolveAsync("ipfs.io", recursive: false) produces "/ipns/website.ipfs.io". Whereas, + /// ResolveAsync("ipfs.io", recursive: true) produces "/ipfs/QmXZz6vQTMiu6UyGxVgpLB6xJdHvvUbhdWagJQNnxXAjpn". + /// + Task ResolveAsync(string name, + bool recursive = false, + CancellationToken cancel = default); + + Task ResolveNameAsync(string name, + bool recursive = false, + bool nocache = false, + CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/INameApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/INameApi.cs new file mode 100644 index 0000000000..41ace671e5 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/INameApi.cs @@ -0,0 +1,120 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Manages the IPNS (Interplanetary Name Space). + /// + /// + /// IPNS is a PKI namespace, where names are the hashes of public keys, and + /// the private key enables publishing new(signed) values. The default name + /// is the node's own , + /// which is the hash of its public key. + /// + /// Name API spec + public interface INameApi + { + /// + /// Publish an IPFS name. + /// + /// + /// The CID or path to the content to publish. + /// + /// + /// Resolve before publishing. Defaults to true. + /// + /// + /// The local key name used to sign the content. Defaults to "self". + /// + /// + /// Duration that the record will be valid for. Defaults to 24 hours. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the of the published content. + /// + Task PublishAsync(string path, + bool resolve = true, + string key = "self", + TimeSpan? lifetime = null, + CancellationToken cancel = default); + + /// + /// Publish an IPFS name. + /// + /// + /// The of the content to publish. + /// + /// + /// The local key name used to sign the content. Defaults to "self". + /// + /// + /// Duration that the record will be valid for. Defaults to 24 hours. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the of the published content. + /// + Task PublishAsync(Cid id, + string key = "self", + TimeSpan? lifetime = null, + CancellationToken cancel = default); + + /// + /// Resolve an IPNS name. + /// + /// + /// An IPNS address, such as: /ipns/ipfs.io or a CID. + /// + /// + /// Resolve until the result is not an IPNS name. Defaults to false. + /// + /// + /// Do not use cached entries. Defaults to false. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the resolved path as a , such as + /// /ipfs/QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao. + /// + Task ResolveAsync(string name, + bool recursive = false, + bool nocache = false, + CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IObjectApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IObjectApi.cs new file mode 100644 index 0000000000..0929521e3d --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IObjectApi.cs @@ -0,0 +1,192 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Manages the IPFS Directed Acrylic Graph. + /// + /// + /// + /// This is being obsoleted by . + /// + /// + /// + /// + /// Catalyst.Ipfs.Core.DagNode + /// + /// Object API spec + public interface IObjectApi + { + /// + /// Creates a new file directory in IPFS. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a to the new directory. + /// + /// + /// Equivalent to NewAsync("unixfs-dir"). + /// + Task NewDirectoryAsync(CancellationToken cancel = default); + + /// + /// Create a new MerkleDAG node, using a specific layout. + /// + /// null or "unixfs-dir". + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a to the new directory. + /// + /// + /// Caveat: So far, only UnixFS object layouts are supported. + /// + Task NewAsync(string template = null, CancellationToken cancel = default); + + /// + /// Fetch a MerkleDAG node. + /// + /// + /// The to the node. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a . + /// + Task GetAsync(Cid id, CancellationToken cancel = default); + + /// + /// Information on a MerkleDag node. + /// + /// + /// The of the node. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// contains the . + /// + Task StatAsync(Cid id, CancellationToken cancel = default); + + /// + /// Store a MerkleDAG node. + /// + /// + /// The opaque data, can be null. + /// + /// + /// The links to other nodes. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a . + /// + Task PutAsync(byte[] data, + IEnumerable links = null, + CancellationToken cancel = default); + + /// + /// Store a MerkleDAG node. + /// + /// A merkle dag + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a . + /// + Task PutAsync(IDagNode node, CancellationToken cancel = default); + + /// + /// Get the data of a MerkleDAG node. + /// + /// + /// The of the node. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a stream of data. + /// + /// + /// The caller must dispose the returned . + /// + Task DataAsync(Cid id, CancellationToken cancel = default); + + /// + /// Get the links of a MerkleDAG node. + /// + /// + /// The unique id of the node. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a sequence of links to the immediate children. + /// + /// + /// LinksAsync returns the immediate child links of the . + /// To get all the children, this code can be used. + /// + /// async Task<List<IMerkleLink>> AllLinksAsync(Cid cid) + /// { + /// var i = 0; + /// var allLinks = new List<IMerkleLink>(); + /// while (cid != null) + /// { + /// var links = await ipfs.Object.LinksAsync(cid); + /// allLinks.AddRange(links); + /// cid = (i < allLinks.Count) ? allLinks[i++].Id : null; + /// } + /// return allLinks; + /// } + /// + /// + Task> LinksAsync(Cid id, CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IPinApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IPinApi.cs new file mode 100644 index 0000000000..0a29c593f0 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IPinApi.cs @@ -0,0 +1,94 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Manage pinned objects (locally stored and permanent). + /// + /// Pin API spec + public interface IPinApi + { + IBlockApi BlockApi { get; set; } + + /// + /// Adds an IPFS object to the pinset and also stores it to the IPFS repo. pinset is the set of hashes currently pinned (not gc'able). + /// + /// + /// A CID or path to an existing object, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" + /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" + /// + /// + /// true to recursively pin links of the object; otherwise, false to only pin + /// the specified object. Default is true. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a sequence of that were pinned. + /// + Task> AddAsync(string path, + bool recursive = true, + CancellationToken cancel = default); + + /// + /// List all the objects pinned to local storage. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a sequence of . + /// + Task> ListAsync(CancellationToken cancel = default); + + /// + /// Unpin an object. + /// + /// + /// The CID of the object. + /// + /// + /// true to recursively unpin links of object; otherwise, false to only unpin + /// the specified object. Default is true. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a sequence of that were unpinned. + /// + Task> RemoveAsync(Cid id, + bool recursive = true, + CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IPubSubApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IPubSubApi.cs new file mode 100644 index 0000000000..ef5660bf10 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IPubSubApi.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Lib.P2P.PubSub; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Allows you to publish messages to a given topic, and also to + /// subscribe to new messages on a given topic. + /// + /// + /// + /// This is an experimental feature. It is not intended in its current state + /// to be used in a production environment. + /// + /// + /// To use, the daemon must be run with '--enable-pubsub-experiment'. + /// + /// + /// Pubsub API spec + public interface IPubSubApi : IPubSub { } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IStatsApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IStatsApi.cs new file mode 100644 index 0000000000..459c0ca6aa --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IStatsApi.cs @@ -0,0 +1,77 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Transports; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Get statistics/diagnostics for the various core components. + /// + public interface IStatsApi + { + /// + /// Get statistics on network bandwidth. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the current . + /// + /// + Task GetBandwidthStatsAsync(CancellationToken cancel = default); + + /// + /// Get statistics on the blocks exchanged with other peers. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the current . + /// + /// + BitswapData GetBitSwapStats(CancellationToken cancel = default); + + /// + /// Get statistics on the repository. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the current . + /// + /// + /// Same as . + /// + /// + Task GetRepositoryStatsAsync(CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/ISwarmApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/ISwarmApi.cs new file mode 100644 index 0000000000..7b6022ff51 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/ISwarmApi.cs @@ -0,0 +1,152 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Manages the swarm of peers. + /// + /// + /// The swarm is a sequence of connected peer nodes. + /// + /// Swarm API spec + public interface ISwarmApi + { + /// + /// Get the peers in the current swarm. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a sequence of peer nodes. + /// + IEnumerable GetSwarmKnownPeers(CancellationToken cancel = default); + + /// + /// Get the peers that are connected to this node. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value + /// is a sequence of Connected Peers. + /// + Task> PeersAsync(CancellationToken cancel = default); + + /// + /// Connect to a peer. + /// + /// + /// An ipfs , such as + /// /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + Task ConnectAsync(MultiAddress address, CancellationToken cancel = default); + + Task ConnectAsync(Peer address, CancellationToken cancel = default); + + /// + /// Disconnect from a peer. + /// + /// + /// An ipfs , such as + /// /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + Task DisconnectAsync(MultiAddress address, CancellationToken cancel = default); + + /// + /// Adds a new address filter. + /// + /// + /// An allowed address. For example "/ip4/104.131.131.82" or + /// "/ip4/192.168.0.0/ipcidr/16". + /// + /// + /// If true the filter will persist across daemon reboots. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the address filter that was added. + /// + /// + Task AddAddressFilterAsync(MultiAddress address, + bool persist = false, + CancellationToken cancel = default); + + /// + /// List all the address filters. + /// + /// + /// If true only persisted filters are listed. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a sequence of addresses filters. + /// + /// + Task> ListAddressFiltersAsync(bool persist = false, + CancellationToken cancel = default); + + /// + /// Delete the specified address filter. + /// + /// + /// For example "/ip4/104.131.131.82" or + /// "/ip4/192.168.0.0/ipcidr/16". + /// + /// + /// If true the filter is also removed from the persistent store. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the address filter that was removed. + /// + /// + Task RemoveAddressFilterAsync(MultiAddress address, + bool persist = false, + CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IUnixFsApi.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IUnixFsApi.cs new file mode 100644 index 0000000000..0abbcab7b2 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IUnixFsApi.cs @@ -0,0 +1,236 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Options; +using Lib.P2P; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Manages the files/directories in IPFS. + /// + /// Files API spec + public interface IUnixFsApi + { + /// + /// Add a local file to the interplanetary file system. + /// + /// + /// The name of the local file. + /// + /// + /// The options when adding data to the IPFS file system. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the file's node. + /// + Task AddFileAsync(string path, + AddFileOptions options = default, + CancellationToken cancel = default); + + /// + /// Add some text to the interplanetary file system. + /// + /// + /// The string to add to IPFS. It is UTF-8 encoded. + /// + /// + /// The options when adding data to the IPFS file system. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the text's node. + /// + Task AddTextAsync(string text, + AddFileOptions options = default, + CancellationToken cancel = default); + + /// + /// Add a to interplanetary file system. + /// + /// + /// The stream of data to add to IPFS. + /// + /// + /// A name for the . + /// + /// + /// The options when adding data to the IPFS file system. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the data's node. + /// + Task AddAsync(Stream stream, + string name = "", + AddFileOptions options = default, + CancellationToken cancel = default); + + /// + /// Add a directory and its files to the interplanetary file system. + /// + /// + /// The path to directory. + /// + /// + /// true to add sub-folders. + /// + /// + /// The options when adding data to the IPFS file system. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the directory's node. + /// + Task AddDirectoryAsync(string path, + bool recursive = true, + AddFileOptions options = default, + CancellationToken cancel = default); + + /// + /// Reads the content of an existing IPFS file as text. + /// + /// + /// A path to an existing file, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" + /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the contents of the as a . + /// + Task ReadAllTextAsync(string path, CancellationToken cancel = default); + + /// + /// Reads an existing IPFS file. + /// + /// + /// An IPFS path to an existing file, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" + /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// a to the file contents. + /// + /// + /// The returned must be disposed. + /// + Task ReadFileAsync(string path, CancellationToken cancel = default); + + /// + /// Reads an existing IPFS file with the specified offset and length. + /// + /// + /// Am IPFS path to an existing file, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" + /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" + /// + /// + /// The position to start reading from. + /// + /// + /// The number of bytes to read. If zero, then the remaining bytes + /// from are read. Defaults to zero. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// a to the file contents. + /// + /// + /// The returned must be disposed. + /// + Task ReadFileAsync(string path, + long offset, + long count = 0, + CancellationToken cancel = default); + + /// + /// Get information about the file or directory. + /// + /// + /// A path to an existing file or directory, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" + /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// an The + /// and are set to null. + /// + Task ListFileAsync(string path, CancellationToken cancel = default); + + /// + /// Download IPFS objects as a TAR archive. + /// + /// + /// An IPFS path to an existing file or directory, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" + /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" + /// + /// + /// If true, the returned stream is compressed with the GZIP algorithm. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// a containing a TAR archive. + /// + /// + /// The returned TAR must be disposed. + /// + /// If the is a directory, then all files and all + /// sub-directories are returned; e.g. it is recursive. + /// + /// + Task GetAsync(string path, + bool compress = false, + CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/IValueStore.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/IValueStore.cs new file mode 100644 index 0000000000..8803bf58be --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/IValueStore.cs @@ -0,0 +1,84 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading; +using System.Threading.Tasks; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// A basic Put/Get interface. + /// + public interface IValueStore + { + /// + /// Gets th value of a key. + /// + /// + /// A byte array representing the name of a key. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation that returns + /// the value of the key as a byte array. + /// + Task GetAsync(byte[] key, CancellationToken cancel = default); + + /// + /// Tries to get the value of a key. + /// + /// + /// A byte array representing the name of a key. + /// + /// + /// A byte array containing the value of the + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation that returns + /// true if the key exists; otherwise, false. + /// + Task TryGetAsync(byte[] key, out byte[] value, CancellationToken cancel = default); + + /// + /// Put the value of a key. + /// + /// + /// A byte array representing the name of a key. + /// + /// + /// A byte array containing the value of the + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task PutAsync(byte[] key, out byte[] value, CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/ObjectStat.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/ObjectStat.cs new file mode 100644 index 0000000000..f727f14d6c --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/ObjectStat.cs @@ -0,0 +1,57 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Information on a DAG node. + /// + /// + public class ObjectStat + { + /// + /// Number of links. + /// + public int LinkCount { get; set; } + + /// + /// Size of the links segment. + /// + public long LinkSize { get; set; } + + /// + /// Size of the raw, encoded data. + /// + public long BlockSize { get; set; } + + /// + /// Siz of the data segment. + /// + public long DataSize { get; set; } + + /// + /// Size of object and its references + /// + public long CumulativeSize { get; set; } + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/RepositoryData.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/RepositoryData.cs new file mode 100644 index 0000000000..7cbc28689e --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/RepositoryData.cs @@ -0,0 +1,71 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// The statistics for . + /// + public class RepositoryData + { + /// + /// The number of blocks in the repository. + /// + /// + /// The number of blocks in the repository. + /// + public ulong NumObjects; + + /// + /// The total number bytes in the repository. + /// + /// + /// The total number bytes in the repository. + /// + public ulong RepoSize; + + /// + /// The fully qualified path to the repository. + /// + /// + /// The directory of the repository. + /// + public string RepoPath; + + /// + /// The version number of the repository. + /// + /// + /// The version number of the repository. + /// + public string Version; + + /// + /// The maximum number of bytes allowed in the repository. + /// + /// + /// Max bytes allowed in the repository. + /// + public ulong StorageMax; + } +} diff --git a/src/Catalyst.Abstractions/Dfs/CoreApi/TransferProgress.cs b/src/Catalyst.Abstractions/Dfs/CoreApi/TransferProgress.cs new file mode 100644 index 0000000000..c4c98e02d7 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/CoreApi/TransferProgress.cs @@ -0,0 +1,48 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; + +namespace Catalyst.Abstractions.Dfs.CoreApi +{ + /// + /// Reports the progress of + /// a transfer operation. + /// + public class TransferProgress + { + /// + /// The name of the item being trasfered. + /// + /// + /// Typically, a relative file path. + /// + public string Name; + + /// + /// The cumuative number of bytes transfered for + /// the . + /// + public ulong Bytes; + } +} diff --git a/src/Catalyst.Abstractions/IO/Observers/IRpcResponseObserver.cs b/src/Catalyst.Abstractions/Dfs/IDagNode.cs similarity index 64% rename from src/Catalyst.Abstractions/IO/Observers/IRpcResponseObserver.cs rename to src/Catalyst.Abstractions/Dfs/IDagNode.cs index 7841213395..d5c70ff0dd 100644 --- a/src/Catalyst.Abstractions/IO/Observers/IRpcResponseObserver.cs +++ b/src/Catalyst.Abstractions/Dfs/IDagNode.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,15 +21,23 @@ #endregion -using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Protocol.Peer; -using DotNetty.Transport.Channels; +using System.Collections.Generic; +using System.IO; using Google.Protobuf; +using Lib.P2P; -namespace Catalyst.Abstractions.IO.Observers +namespace Catalyst.Abstractions.Dfs { - public interface IRpcResponseObserver : IResponseMessageObserver + public interface IDagNode : IMerkleNode { - void HandleResponseObserver(IMessage messageDto, IChannelHandlerContext channelHandlerContext, PeerId senderPeerIdentifier, ICorrelationId correlationId); + Cid Id { get; set; } + byte[] ToArray(); + + IDagNode AddLink(IMerkleLink link); + + void Write(CodedOutputStream stream); + void Write(Stream stream); + + IDagNode AddLinks(IEnumerable links); } } diff --git a/src/Catalyst.Abstractions/Dfs/IDfs.cs b/src/Catalyst.Abstractions/Dfs/IDfs.cs deleted file mode 100644 index 80bd845bbf..0000000000 --- a/src/Catalyst.Abstractions/Dfs/IDfs.cs +++ /dev/null @@ -1,72 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using LibP2P; - -namespace Catalyst.Abstractions.Dfs -{ - /// - /// Provides read-write access to a distributed file system. - /// - public interface IDfs - { - /// - /// Add some text to the distributed file system. - /// - /// The text to add to the DFS. - /// A cancellation token that can be used to cancel the work. - /// The unique ID to the content created on the DFS. - Task AddTextAsync(string content, CancellationToken cancellationToken = default); - - /// - /// Reads the content of an existing file on the DFS as a UTF8 string. - /// - /// Dfs content address - /// A cancellation token that can be used to cancel the work. - /// The content of the DFS file as a UTF8 encoded string. - Task ReadTextAsync(Cid cid, CancellationToken cancellationToken = default); - - /// - /// Adds content from a stream of data to the DFS. - /// - /// A stream containing the data to be stored on the DFS. - /// A name for the - /// A cancellation token that can be used to cancel the work. - /// The unique ID to the newly added content on the DFS. - Task AddAsync(Stream content, string name = "", CancellationToken cancellationToken = default); - - /// - /// Streams the content of an existing file on the DFS. - /// - /// Dfs content address - /// A cancellation token that can be used to cancel the work. - /// A to the content of the file. - /// - /// The returned must be disposed. - /// - Task ReadAsync(Cid cid, CancellationToken cancellationToken = default); - } -} diff --git a/src/Catalyst.Abstractions/Dfs/IDfsGateway.cs b/src/Catalyst.Abstractions/Dfs/IDfsGateway.cs index b75f275e36..a24eb2c7a8 100644 --- a/src/Catalyst.Abstractions/Dfs/IDfsGateway.cs +++ b/src/Catalyst.Abstractions/Dfs/IDfsGateway.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Dfs/IDfsService.cs b/src/Catalyst.Abstractions/Dfs/IDfsService.cs new file mode 100644 index 0000000000..939eba955f --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/IDfsService.cs @@ -0,0 +1,81 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.Dfs.BlockExchange; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Dfs.Migration; +using Catalyst.Abstractions.Options; +using Lib.P2P; +using Lib.P2P.Protocols; +using Lib.P2P.PubSub; +using Lib.P2P.Routing; + +namespace Catalyst.Abstractions.Dfs +{ + /// + /// Provides read-write access to a distributed file system. + /// + public interface IDfsService : ICoreApi, IService, IDisposable + { + bool IsStarted { get; } + + IMigrationManager MigrationManager { get; set; } + + /// + /// Provides access to the local peer. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a . + /// + Peer LocalPeer { get; } + + DfsOptions Options { get; set; } + + /// + /// Determines latency to a peer. + /// + Ping1 PingService { get; } + + /// + /// Manages communication with other peers. + /// + ISwarmService SwarmService { get; } + + /// + /// Manages publishng and subscribing to messages. + /// + IPubSubService PubSubService { get; } + + /// + /// Exchange blocks with other peers. + /// + IBitswapService BitSwapService { get; } + + /// + /// Finds information with a distributed hash table. + /// + IDhtService DhtService { get; } + } +} diff --git a/src/Catalyst.Abstractions/Dfs/IFileSystemLink.cs b/src/Catalyst.Abstractions/Dfs/IFileSystemLink.cs new file mode 100644 index 0000000000..d0fbfaab7c --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/IFileSystemLink.cs @@ -0,0 +1,30 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.Dfs +{ + /// + /// A link to another file system node in IPFS. + /// + public interface IFileSystemLink : IMerkleLink { } +} diff --git a/src/Catalyst.Abstractions/Dfs/IFileSystemNode.cs b/src/Catalyst.Abstractions/Dfs/IFileSystemNode.cs new file mode 100644 index 0000000000..2167040701 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/IFileSystemNode.cs @@ -0,0 +1,40 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.Dfs +{ + /// + /// A Directed Acyclic Graph (DAG) for IPFS file system node. + /// + public interface IFileSystemNode : IMerkleNode + { + /// + /// Determines if the node is a directory (folder). + /// + /// + /// true if the node is a directory; Otherwise false, + /// it is some type of a file. + /// + bool IsDirectory { get; } + } +} diff --git a/src/Catalyst.Abstractions/Dfs/IIpfsAdapter.cs b/src/Catalyst.Abstractions/Dfs/IIpfsAdapter.cs index 37eef60d75..24ca87f6db 100644 --- a/src/Catalyst.Abstractions/Dfs/IIpfsAdapter.cs +++ b/src/Catalyst.Abstractions/Dfs/IIpfsAdapter.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,7 @@ #endregion using System; -using TheDotNetLeague.Ipfs.Core.Lib; +using Catalyst.Abstractions.Dfs.CoreApi; namespace Catalyst.Abstractions.Dfs { diff --git a/src/Catalyst.Abstractions/Dfs/ILinkedNode.cs b/src/Catalyst.Abstractions/Dfs/ILinkedNode.cs new file mode 100644 index 0000000000..240abab77a --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/ILinkedNode.cs @@ -0,0 +1,34 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.Dfs +{ + /// + /// InterPlanetary Linked Data. + /// + /// + /// Not yet ready for prime time. + /// + /// + public interface ILinkedNode : IMerkleNode { } +} diff --git a/src/Catalyst.Abstractions/Dfs/IMerkleLink.cs b/src/Catalyst.Abstractions/Dfs/IMerkleLink.cs new file mode 100644 index 0000000000..59a3335e91 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/IMerkleLink.cs @@ -0,0 +1,59 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Lib.P2P; + +namespace Catalyst.Abstractions.Dfs +{ + /// + /// A link to another node in IPFS. + /// + public interface IMerkleLink + { + /// + /// A name associated with the linked node. + /// + /// A or null. + /// + /// + /// IPFS considers a null name different from a + /// name; + /// + /// + string Name { get; } + + /// + /// The unique ID of the link. + /// + /// + /// A of the content. + /// + Cid Id { get; } + + /// + /// The serialised size (in bytes) of the linked node. + /// + /// Number of bytes. + long Size { get; } + } +} diff --git a/src/Catalyst.Abstractions/Dfs/IMerkleNode.cs b/src/Catalyst.Abstractions/Dfs/IMerkleNode.cs new file mode 100644 index 0000000000..1dad373e39 --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/IMerkleNode.cs @@ -0,0 +1,71 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using Lib.P2P; + +namespace Catalyst.Abstractions.Dfs +{ + /// + /// A Directed Acyclic Graph (DAG) in IPFS. + /// + /// + /// A MerkleNode has a sequence of navigable + /// and some data ( + /// or ). + /// + /// + /// The type of used by this node. + /// + /// + /// + public interface IMerkleNode : IDataBlock + where TLink : IMerkleLink + { + /// + /// Links to other nodes. + /// + /// + /// A sequence of . + /// + /// + /// It is never null. + /// + /// The links are sorted ascending by . A null + /// name is compared as "". + /// + /// + IEnumerable Links { get; } + + /// + /// Returns a link to the node. + /// + /// + /// A for the link; defaults to "". + /// + /// + /// A new to the node. + /// + TLink ToLink(string name = ""); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/Migration/IMigration.cs b/src/Catalyst.Abstractions/Dfs/Migration/IMigration.cs new file mode 100644 index 0000000000..46c057262f --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/Migration/IMigration.cs @@ -0,0 +1,70 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Options; + +namespace Catalyst.Abstractions.Dfs.Migration +{ + /// + /// Provides a migration path to the repository. + /// + public interface IMigration + { + /// + /// The repository version that is created. + /// + int Version { get; } + + /// + /// Indicates that an upgrade can be performed. + /// + bool CanUpgrade { get; } + + /// + /// Indicates that an downgrade can be performed. + /// + bool CanDowngrade { get; } + + /// + /// Upgrade the repository. + /// + /// + /// The IPFS system to upgrade. + /// + /// + /// + Task UpgradeAsync(RepositoryOptions options, CancellationToken cancel = default); + + /// + /// Downgrade the repository. + /// + /// + /// The IPFS system to downgrade. + /// + /// + /// + Task DowngradeAsync(RepositoryOptions options, CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/Migration/IMigrationManager.cs b/src/Catalyst.Abstractions/Dfs/Migration/IMigrationManager.cs new file mode 100644 index 0000000000..196bd3604e --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/Migration/IMigrationManager.cs @@ -0,0 +1,58 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Catalyst.Abstractions.Dfs.Migration +{ + public interface IMigrationManager + { + /// + /// The list of migrations that can be performed. + /// + List Migrations { get; } + + /// + /// Gets the latest supported version number of a repository. + /// + int LatestVersion { get; } + + /// + /// Gets the current vesion number of the repository. + /// + int CurrentVersion { get; } + + /// + /// Upgrade/downgrade to the specified version. + /// + /// + /// The required version of the repository. + /// + /// + /// + /// + Task MirgrateToVersionAsync(int version, CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Dfs/NamedContent.cs b/src/Catalyst.Abstractions/Dfs/NamedContent.cs new file mode 100644 index 0000000000..8a5c76c62c --- /dev/null +++ b/src/Catalyst.Abstractions/Dfs/NamedContent.cs @@ -0,0 +1,50 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Dfs.CoreApi; + +namespace Catalyst.Abstractions.Dfs +{ + /// + /// Content that has an associated name. + /// + /// + public class NamedContent + { + /// + /// Path to the name. + /// + /// + /// Typically /ipns/.... + /// + public string NamePath { get; set; } + + /// + /// Path to the content. + /// + /// + /// Typically /ipfs/.... + /// + public string ContentPath { get; set; } + } +} diff --git a/src/Catalyst.Abstractions/Enumerator/Enumeration.cs b/src/Catalyst.Abstractions/Enumerator/Enumeration.cs index a89cb60326..bb37f3f5fe 100644 --- a/src/Catalyst.Abstractions/Enumerator/Enumeration.cs +++ b/src/Catalyst.Abstractions/Enumerator/Enumeration.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Enumerator/IEnumeration.cs b/src/Catalyst.Abstractions/Enumerator/IEnumeration.cs index 276e5b9127..daef017175 100644 --- a/src/Catalyst.Abstractions/Enumerator/IEnumeration.cs +++ b/src/Catalyst.Abstractions/Enumerator/IEnumeration.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/FileSystem/IFileSystem.cs b/src/Catalyst.Abstractions/FileSystem/IFileSystem.cs index d93bce0f20..dcb4812f1e 100644 --- a/src/Catalyst.Abstractions/FileSystem/IFileSystem.cs +++ b/src/Catalyst.Abstractions/FileSystem/IFileSystem.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -34,10 +34,6 @@ public interface IFileSystem : System.IO.Abstractions.IFileSystem Task WriteTextFileToCddSubDirectoryAsync(string fileName, string subDirectory, string contents); bool DataFileExists(string fileName); - bool DataFileExistsInSubDirectory(string fileName, string subDirectory); - - string ReadTextFromCddFile(string fileName); - string ReadTextFromCddSubDirectoryFile(string fileName, string subDirectory); bool SetCurrentPath(string path); diff --git a/src/Catalyst.Abstractions/Hashing/HashExtensions.cs b/src/Catalyst.Abstractions/Hashing/HashExtensions.cs new file mode 100644 index 0000000000..efff6bd75c --- /dev/null +++ b/src/Catalyst.Abstractions/Hashing/HashExtensions.cs @@ -0,0 +1,127 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Buffers; +using System.Text; +using DotNetty.Common.Utilities; +using Google.Protobuf; +using MultiFormats; + +namespace Catalyst.Abstractions.Hashing +{ + public static class HashExtensions + { + /// + /// Serializes the , appends suffix and returns the hash of it. + /// + /// The hash provider. + /// The protocol message. + /// The suffix that should be appended to the message. + /// + public static MultiHash ComputeMultiHash(this IHashProvider provider, IMessage message, byte[] suffix) + { + ProtoPreconditions.CheckNotNull(message, nameof(message)); + var calculateSize = message.CalculateSize(); + + var required = calculateSize + suffix.Length; + var array = ArrayPool.Shared.Rent(required); + + try + { + using (CodedOutputStream output = new(array)) + { + message.WriteTo(output); + } + + suffix.CopyTo(array, calculateSize); + + var result = provider.ComputeMultiHash(array, 0, required); + return result; + } + finally + { + ArrayPool.Shared.Return(array); + } + } + + public static MultiHash ComputeMultiHash(this IHashProvider provider, string message, byte[] suffix) + { + return ComputeMultiHash(provider, Encoding.UTF8.GetBytes(message), suffix); + } + + public static MultiHash ComputeMultiHash(this IHashProvider provider, byte[] message, byte[] suffix) + { + var calculateSize = message.Length; + + var required = calculateSize + suffix.Length; + var array = ArrayPool.Shared.Rent(required); + + try + { + message.CopyTo(array, 0); + suffix.CopyTo(array, calculateSize); + + var result = provider.ComputeMultiHash(array, 0, required); + return result; + } + finally + { + ArrayPool.Shared.Return(array); + } + } + + public static MultiHash ComputeMultiHash(this IHashProvider provider, MultiAddress address, byte[] suffix) + { + return ComputeMultiHash(provider, address.ToArray(), suffix); + } + + /// + /// Serializes the returns the hash of it. + /// + /// The hash provider. + /// The protocol message. + /// + public static MultiHash ComputeMultiHash(this IHashProvider provider, IMessage message) + { + ProtoPreconditions.CheckNotNull(message, nameof(message)); + var required = message.CalculateSize(); + + var array = ArrayPool.Shared.Rent(required); + + try + { + using (CodedOutputStream output = new(array)) + { + message.WriteTo(output); + } + + var result = provider.ComputeMultiHash(array, 0, required); + return result; + } + finally + { + ArrayPool.Shared.Return(array); + } + } + } +} diff --git a/src/Catalyst.Abstractions/Hashing/IHashProvider.cs b/src/Catalyst.Abstractions/Hashing/IHashProvider.cs index afe5d09cb1..fc16e261c9 100644 --- a/src/Catalyst.Abstractions/Hashing/IHashProvider.cs +++ b/src/Catalyst.Abstractions/Hashing/IHashProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,18 +23,21 @@ using System.Collections.Generic; using System.IO; -using TheDotNetLeague.MultiFormats.MultiHash; +using MultiFormats; +using MultiFormats.Registry; namespace Catalyst.Abstractions.Hashing { public interface IHashProvider { HashingAlgorithm HashingAlgorithm { set; get; } + MultiHash ComputeUtf8MultiHash(string data); MultiHash ComputeMultiHash(Stream data); MultiHash ComputeMultiHash(byte[] data); MultiHash ComputeMultiHash(IEnumerable content); MultiHash Cast(byte[] data); bool IsValidHash(byte[] data); + MultiHash ComputeMultiHash(byte[] data, int offset, int count); } } diff --git a/src/Catalyst.Abstractions/ICatalystNode.cs b/src/Catalyst.Abstractions/ICatalystNode.cs index 1733bd7ec9..11f954ff9c 100644 --- a/src/Catalyst.Abstractions/ICatalystNode.cs +++ b/src/Catalyst.Abstractions/ICatalystNode.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/IO/Events/ISocketClientRegistryEvent.cs b/src/Catalyst.Abstractions/IO/Events/ISocketClientRegistryEvent.cs index f16dc2fb5a..e724f490f1 100644 --- a/src/Catalyst.Abstractions/IO/Events/ISocketClientRegistryEvent.cs +++ b/src/Catalyst.Abstractions/IO/Events/ISocketClientRegistryEvent.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/IO/Events/ITransactionReceivedEvent.cs b/src/Catalyst.Abstractions/IO/Events/ITransactionReceivedEvent.cs index 694f4277fc..7e3bbc9964 100644 --- a/src/Catalyst.Abstractions/IO/Events/ITransactionReceivedEvent.cs +++ b/src/Catalyst.Abstractions/IO/Events/ITransactionReceivedEvent.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,6 +28,6 @@ namespace Catalyst.Abstractions.IO.Events { public interface ITransactionReceivedEvent { - ResponseCode OnTransactionReceived(ProtocolMessage broadcast); + ResponseCode OnTransactionReceived(ProtocolMessage message, bool broadcast); } } diff --git a/src/Catalyst.Abstractions/IO/Handlers/IInboundMessageHandler.cs b/src/Catalyst.Abstractions/IO/Handlers/IInboundMessageHandler.cs new file mode 100644 index 0000000000..4ebe1c1741 --- /dev/null +++ b/src/Catalyst.Abstractions/IO/Handlers/IInboundMessageHandler.cs @@ -0,0 +1,27 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.IO.Handlers +{ + public interface IInboundMessageHandler : IMessageHandler { } +} diff --git a/src/Catalyst.Core.Lib/Mempool/Models/MempoolItem.cs b/src/Catalyst.Abstractions/IO/Handlers/IMessageHandler.cs similarity index 77% rename from src/Catalyst.Core.Lib/Mempool/Models/MempoolItem.cs rename to src/Catalyst.Abstractions/IO/Handlers/IMessageHandler.cs index c708d42d17..808c8ac301 100644 --- a/src/Catalyst.Core.Lib/Mempool/Models/MempoolItem.cs +++ b/src/Catalyst.Abstractions/IO/Handlers/IMessageHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,13 +21,13 @@ #endregion -using Catalyst.Abstractions.Mempool.Models; using Catalyst.Protocol.Wire; +using System.Threading.Tasks; -namespace Catalyst.Core.Lib.Mempool.Models +namespace Catalyst.Abstractions.IO.Handlers { - public class MempoolItem : IMempoolItem + public interface IMessageHandler { - public TransactionBroadcast Transaction { get; set; } + Task ProcessAsync(ProtocolMessage message); } } diff --git a/src/Catalyst.Abstractions/IO/Handlers/IObservableServiceHandler.cs b/src/Catalyst.Abstractions/IO/Handlers/IObservableServiceHandler.cs index a1f5cbb9bf..a4f04ac791 100644 --- a/src/Catalyst.Abstractions/IO/Handlers/IObservableServiceHandler.cs +++ b/src/Catalyst.Abstractions/IO/Handlers/IObservableServiceHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,10 +23,9 @@ using System; using Catalyst.Abstractions.IO.Observers; -using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; namespace Catalyst.Abstractions.IO.Handlers { - public interface IObservableServiceHandler : IChannelHandler, IObservableMessageStreamer, IDisposable { } + public interface IObservableServiceHandler : IChannelHandler, IObservableMessageStreamer, IDisposable { } } diff --git a/src/Catalyst.Abstractions/IO/Handlers/IOutboundMessageHandler.cs b/src/Catalyst.Abstractions/IO/Handlers/IOutboundMessageHandler.cs new file mode 100644 index 0000000000..336dc07a2a --- /dev/null +++ b/src/Catalyst.Abstractions/IO/Handlers/IOutboundMessageHandler.cs @@ -0,0 +1,27 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.IO.Handlers +{ + public interface IOutboundMessageHandler : IMessageHandler { } +} diff --git a/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICacheEvictionEvent.cs b/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICacheEvictionEvent.cs index 8059b859b5..133b4af902 100644 --- a/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICacheEvictionEvent.cs +++ b/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICacheEvictionEvent.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,12 +23,13 @@ using Catalyst.Protocol.Peer; using Google.Protobuf; +using MultiFormats; namespace Catalyst.Abstractions.IO.Messaging.Correlation { public interface ICacheEvictionEvent where T : IMessage { T EvictedContent { get; } - PeerId PeerId { get; } + MultiAddress Address { get; } } } diff --git a/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICorrelatableMessage.cs b/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICorrelatableMessage.cs index 6eb92214e9..e793ef40f4 100644 --- a/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICorrelatableMessage.cs +++ b/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICorrelatableMessage.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,13 +24,14 @@ using System; using Catalyst.Protocol.Peer; using Google.Protobuf; +using MultiFormats; namespace Catalyst.Abstractions.IO.Messaging.Correlation { public interface ICorrelatableMessage where T : IMessage { T Content { get; set; } - PeerId Recipient { get; set; } + MultiAddress Recipient { get; set; } DateTimeOffset SentAt { get; set; } } } diff --git a/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICorrelationId.cs b/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICorrelationId.cs index 4c5793ab4f..34b1e09110 100644 --- a/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICorrelationId.cs +++ b/src/Catalyst.Abstractions/IO/Messaging/Correlation/ICorrelationId.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/IO/Messaging/Correlation/IMessageCorrelationManager.cs b/src/Catalyst.Abstractions/IO/Messaging/Correlation/IMessageCorrelationManager.cs index de240f72bb..0438f300e8 100644 --- a/src/Catalyst.Abstractions/IO/Messaging/Correlation/IMessageCorrelationManager.cs +++ b/src/Catalyst.Abstractions/IO/Messaging/Correlation/IMessageCorrelationManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/IO/Observables/ISocketClientRegistryEvent.cs b/src/Catalyst.Abstractions/IO/Observables/ISocketClientRegistryEvent.cs index 77a19cedd2..ce760aac32 100644 --- a/src/Catalyst.Abstractions/IO/Observables/ISocketClientRegistryEvent.cs +++ b/src/Catalyst.Abstractions/IO/Observables/ISocketClientRegistryEvent.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/IO/Observers/IBroadcastObserver.cs b/src/Catalyst.Abstractions/IO/Observers/IBroadcastObserver.cs index 9ce9d15aa9..fbf4655890 100644 --- a/src/Catalyst.Abstractions/IO/Observers/IBroadcastObserver.cs +++ b/src/Catalyst.Abstractions/IO/Observers/IBroadcastObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,13 +21,12 @@ #endregion -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Protocol.Wire; namespace Catalyst.Abstractions.IO.Observers { - public interface IBroadcastObserver : IMessageObserver + public interface IBroadcastObserver : IMessageObserver { - void HandleBroadcast(IObserverDto messageDto); + void HandleBroadcast(ProtocolMessage message); } } diff --git a/src/Catalyst.Abstractions/IO/Observers/IMessageObserver.cs b/src/Catalyst.Abstractions/IO/Observers/IMessageObserver.cs index 98e6725932..248255248d 100644 --- a/src/Catalyst.Abstractions/IO/Observers/IMessageObserver.cs +++ b/src/Catalyst.Abstractions/IO/Observers/IMessageObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,13 +22,11 @@ #endregion using System; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Protocol.Wire; namespace Catalyst.Abstractions.IO.Observers { - public interface IMessageObserver : IObserver> + public interface IMessageObserver : IObserver { - void StartObserving(IObservable> messageStream); + void StartObserving(IObservable messageStream); } } diff --git a/src/Catalyst.Abstractions/IO/Observers/IObservableMessageStreamer.cs b/src/Catalyst.Abstractions/IO/Observers/IObservableMessageStreamer.cs index 5c48d826a6..f793231231 100644 --- a/src/Catalyst.Abstractions/IO/Observers/IObservableMessageStreamer.cs +++ b/src/Catalyst.Abstractions/IO/Observers/IObservableMessageStreamer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,16 +22,14 @@ #endregion using System; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Google.Protobuf; namespace Catalyst.Abstractions.IO.Observers { - public interface IObservableMessageStreamer where T : IMessage + public interface IObservableMessageStreamer { /// /// Message stream /// - IObservable> MessageStream { get; } + IObservable MessageStream { get; } } } diff --git a/src/Catalyst.Abstractions/IO/Observers/IP2PMessageObserver.cs b/src/Catalyst.Abstractions/IO/Observers/IP2PMessageObserver.cs index e02ac3439b..e2819d707a 100644 --- a/src/Catalyst.Abstractions/IO/Observers/IP2PMessageObserver.cs +++ b/src/Catalyst.Abstractions/IO/Observers/IP2PMessageObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,9 @@ #endregion +using Catalyst.Protocol.Wire; + namespace Catalyst.Abstractions.IO.Observers { - public interface IP2PMessageObserver : IMessageObserver { } + public interface IP2PMessageObserver : IMessageObserver { } } diff --git a/src/Catalyst.Abstractions/IO/Observers/IRequestMessageObserver.cs b/src/Catalyst.Abstractions/IO/Observers/IRequestMessageObserver.cs index cd60d13aa4..93e2b9b6cf 100644 --- a/src/Catalyst.Abstractions/IO/Observers/IRequestMessageObserver.cs +++ b/src/Catalyst.Abstractions/IO/Observers/IRequestMessageObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,7 +25,7 @@ namespace Catalyst.Abstractions.IO.Observers { - public interface IRequestMessageObserver : IMessageObserver + public interface IRequestMessageObserver : IMessageObserver { IPeerSettings PeerSettings { get; } } diff --git a/src/Catalyst.Abstractions/IO/Observers/IResponseMessageObserver.cs b/src/Catalyst.Abstractions/IO/Observers/IResponseMessageObserver.cs index 676dc6b139..e072af0b53 100644 --- a/src/Catalyst.Abstractions/IO/Observers/IResponseMessageObserver.cs +++ b/src/Catalyst.Abstractions/IO/Observers/IResponseMessageObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,5 +23,5 @@ namespace Catalyst.Abstractions.IO.Observers { - public interface IResponseMessageObserver : IMessageObserver { } + public interface IResponseMessageObserver : IMessageObserver { } } diff --git a/src/Catalyst.Abstractions/KeySigner/IKeySigner.cs b/src/Catalyst.Abstractions/KeySigner/IKeySigner.cs index fd31fa48a5..c22a30dcca 100644 --- a/src/Catalyst.Abstractions/KeySigner/IKeySigner.cs +++ b/src/Catalyst.Abstractions/KeySigner/IKeySigner.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,8 +21,9 @@ #endregion +using System; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Types; using Catalyst.Protocol.Cryptography; using ISignature = Catalyst.Abstractions.Cryptography.ISignature; @@ -30,20 +31,17 @@ namespace Catalyst.Abstractions.KeySigner { public interface IKeySigner { - /// - /// Takes a KeyStore implementation to support both local and remote KeyStores'. - /// - IKeyStore KeyStore { get; } - /// /// Takes the crypto library implementation the nodes using. /// ICryptoContext CryptoContext { get; } - - ISignature Sign(byte[] data, SigningContext signingContext); + + IPrivateKey GetPrivateKey(KeyRegistryTypes keyIdentifier); + + ISignature Sign(ReadOnlySpan data, SigningContext signingContext); /// Verifies a message signature. /// - bool Verify(ISignature signature, byte[] message, SigningContext signingContext); + bool Verify(ISignature signature, ReadOnlySpan data, SigningContext signingContext); } } diff --git a/src/Catalyst.Abstractions/KeySigner/KeySignerExtensions.cs b/src/Catalyst.Abstractions/KeySigner/KeySignerExtensions.cs new file mode 100644 index 0000000000..f9ce927eb3 --- /dev/null +++ b/src/Catalyst.Abstractions/KeySigner/KeySignerExtensions.cs @@ -0,0 +1,47 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Cryptography; +using Catalyst.Protocol; +using Catalyst.Protocol.Cryptography; +using Google.Protobuf; + +namespace Catalyst.Abstractions.KeySigner +{ + public static class KeySignerExtensions + { + public static ISignature Sign(this IKeySigner crypto, IMessage message, SigningContext context) + { + using var pooled = message.SerializeToPooledBytes(); + + return crypto.Sign(pooled.Span, context); + } + + public static bool Verify(this IKeySigner crypto, ISignature signature, IMessage message, SigningContext context) + { + using var pooled = message.SerializeToPooledBytes(); + + return crypto.Verify(signature, pooled.Span, context); + } + } +} diff --git a/src/Catalyst.Abstractions/Keystore/IKeyApi.cs b/src/Catalyst.Abstractions/Keystore/IKeyApi.cs new file mode 100644 index 0000000000..f081e005d8 --- /dev/null +++ b/src/Catalyst.Abstractions/Keystore/IKeyApi.cs @@ -0,0 +1,148 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Cryptography; +using Org.BouncyCastle.Crypto; + +namespace Catalyst.Abstractions.Keystore +{ + /// + /// Manages cryptographic keys. + /// + /// + /// + /// The Key API is work in progress! There be dragons here. + /// + /// + /// Key API spec + public interface IKeyApi + { + /// + /// Creates a new key. + /// + /// + /// The local name of the key. + /// + /// + /// The type of key to create; "rsa" or "ed25519". + /// + /// + /// The size, in bits, of the key. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the key that was created. + /// + Task CreateAsync(string name, + string keyType, + int size, + CancellationToken cancel = default); + + /// + /// List all the keys. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a sequence of IPFS keys. + /// + Task> ListAsync(CancellationToken cancel = default); + + /// + /// Delete the specified key. + /// + /// + /// The local name of the key. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the key that was deleted. + /// + Task RemoveAsync(string name, CancellationToken cancel = default); + + /// + /// Rename the specified key. + /// + /// + /// The local name of the key. + /// + /// + /// The new local name of the key. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a sequence of IPFS keys that were renamed. + /// + Task RenameAsync(string oldName, string newName, CancellationToken cancel = default); + + /// + /// Export a key to a PEM encoded password protected PKCS #8 container. + /// + /// + /// The local name of the key. + /// + /// + /// The PEM's password. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the password protected PEM string. + /// + Task ExportAsync(string name, char[] password, CancellationToken cancel = default); + + Task ImportAsync(string name, string pem, char[] password, CancellationToken cancel = default); + + Task GetKeyAsync(string self); + Task GetDfsPublicKeyAsync(string name, CancellationToken cancel = default); + + Task GetPrivateKeyAsync(string self); + + Task CreateProtectedDataAsync(string keyName, + byte[] plainText, + CancellationToken cancel = default); + + Task ReadProtectedDataAsync(byte[] cipherText, + CancellationToken cancel = default); + + Task SetPassphraseAsync(SecureString passphrase, + CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Keystore/IKeyRegistry.cs b/src/Catalyst.Abstractions/Keystore/IKeyRegistry.cs index 3a1c2e75c2..651d9b96d1 100644 --- a/src/Catalyst.Abstractions/Keystore/IKeyRegistry.cs +++ b/src/Catalyst.Abstractions/Keystore/IKeyRegistry.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,9 @@ #endregion +using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.Registry; using Catalyst.Abstractions.Types; -using IPrivateKey = Catalyst.Abstractions.Cryptography.IPrivateKey; namespace Catalyst.Abstractions.Keystore { diff --git a/src/Catalyst.Abstractions/Keystore/IKeyStoreService.cs b/src/Catalyst.Abstractions/Keystore/IKeyStoreService.cs new file mode 100644 index 0000000000..bddc0e3a12 --- /dev/null +++ b/src/Catalyst.Abstractions/Keystore/IKeyStoreService.cs @@ -0,0 +1,210 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Options; +using Org.BouncyCastle.Crypto; + +namespace Catalyst.Abstractions.Keystore +{ + public interface IKeyStoreService + { + /// + /// Import a key from a PEM encoded password protected PKCS #8 container. + /// + /// + /// The local name of the key. + /// + /// + /// The PEM encoded PKCS #8 container. + /// + /// + /// The 's password. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the newly imported key. + /// + Task ImportAsync(string name, string pem, char[] password = null, CancellationToken cancel = default); + + /// + /// Gets the IPFS encoded public key for the specified key. + /// + /// + /// The local name of the key. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the IPFS encoded public key. + /// + /// + /// The IPFS public key is the base-64 encoding of a protobuf encoding containing + /// a type and the DER encoding of the PKCS Subject Public Key Info. + /// + /// + Task GetDfsPublicKeyAsync(string name, CancellationToken cancel = default); + + /// + /// Find a key by its name. + /// + /// + /// The local name of the key. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// an or null if the the key is not defined. + /// + Task FindKeyByNameAsync(string name, CancellationToken cancel = default); + + /// + /// The configuration options. + /// + KeyChainOptions Options { get; set; } + + /// + /// Encrypt data as CMS protected data. + /// + /// + /// The key name to protect the with. + /// + /// + /// The data to protect. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the cipher text of the . + /// + /// + /// Cryptographic Message Syntax (CMS), aka PKCS #7 and + /// RFC 5652, + /// describes an encapsulation syntax for data protection. It + /// is used to digitally sign, digest, authenticate, and/or encrypt + /// arbitrary message content. + /// + Task CreateProtectedDataAsync(string keyName, byte[] plainText, CancellationToken cancel = default); + + /// + /// Decrypt CMS protected data. + /// + /// + /// The protected CMS data. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the plain text byte array of the protected data. + /// + /// + /// When the required private key, to decrypt the data, is not foumd. + /// + /// + /// Cryptographic Message Syntax (CMS), aka PKCS #7 and + /// RFC 5652, + /// describes an encapsulation syntax for data protection. It + /// is used to digitally sign, digest, authenticate, and/or encrypt + /// arbitrary message content. + /// + Task ReadProtectedDataAsync(byte[] cipherText, CancellationToken cancel = default); + + /// + /// Sets the passphrase for the key chain. + /// + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// When the is wrong. + /// + /// + /// The is used to generate a DEK (derived encryption + /// key). The DEK is then used to encrypt the stored keys. + /// + /// Neither the nor the DEK are stored. + /// + /// + Task SetPassphraseAsync(SecureString passphrase, CancellationToken cancel = default); + + /// + Task CreateAsync(string name, string keyType, int size, CancellationToken cancel = default); + + /// + Task ExportAsync(string name, char[] password, CancellationToken cancel = default); + + /// + Task> ListAsync(CancellationToken cancel = default); + + /// + Task RemoveAsync(string name, CancellationToken cancel = default); + + /// + Task RenameAsync(string oldName, string newName, CancellationToken cancel = default); + + /// + /// Gets the Bouncy Castle representation of the private key. + /// + /// + /// The local name of key. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the private key as an AsymmetricKeyParameter. + /// + Task GetPrivateKeyAsync(string name, CancellationToken cancel = default); + + /// + /// Create a X509 certificate for the specified key. + /// + /// + /// The key name. + /// + /// + /// + Task CreateCertificateAsync(string keyName, CancellationToken cancel = default); + } +} diff --git a/src/Catalyst.Abstractions/Keystore/IKeystore.cs b/src/Catalyst.Abstractions/Keystore/IKeystore.cs index b639c3abef..d0f8197e22 100644 --- a/src/Catalyst.Abstractions/Keystore/IKeystore.cs +++ b/src/Catalyst.Abstractions/Keystore/IKeystore.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,7 +32,7 @@ public interface IKeyStore { IPrivateKey KeyStoreDecrypt(KeyRegistryTypes keyIdentifier); - Task KeyStoreGenerateAsync(NetworkType networkType, KeyRegistryTypes keyIdentifier); + Task KeyStoreGenerateAsync(KeyRegistryTypes keyIdentifier); Task KeyStoreEncryptAsync(IPrivateKey privateKey, NetworkType networkType, KeyRegistryTypes keyIdentifier); } diff --git a/src/Catalyst.Abstractions/Keystore/ISigningContextProvider.cs b/src/Catalyst.Abstractions/Keystore/ISigningContextProvider.cs index 08d14f895a..f1c13c437f 100644 --- a/src/Catalyst.Abstractions/Keystore/ISigningContextProvider.cs +++ b/src/Catalyst.Abstractions/Keystore/ISigningContextProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Kvm/CidJsonConverter.cs b/src/Catalyst.Abstractions/Kvm/CidJsonConverter.cs new file mode 100644 index 0000000000..3d94b3e49c --- /dev/null +++ b/src/Catalyst.Abstractions/Kvm/CidJsonConverter.cs @@ -0,0 +1,62 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Lib.P2P; +using MultiFormats; +using Nethermind.Core.Crypto; + +using Nethermind.Serialization.Json; +using Newtonsoft.Json; + +namespace Catalyst.Abstractions.Kvm +{ + public class CidJsonConverter : JsonConverter + { + static readonly KeccakConverter Converter = new(); + + public override void WriteJson(JsonWriter writer, Cid value, JsonSerializer serializer) + { + Converter.WriteJson(writer, ToKeccak(value), serializer); + } + + public override Cid ReadJson(JsonReader reader, Type objectType, Cid existingValue, bool hasExistingValue, JsonSerializer serializer) + { + var keccak = Converter.ReadJson(reader, typeof(Keccak), ToKeccak(existingValue), hasExistingValue, serializer); + if (keccak == null) + { + return null; + } + + return new Cid + { + Version = 1, + Encoding = "base32", + ContentType = "dag-pb", + Hash = new MultiHash("blake2b-256", keccak.Bytes) + }; + } + + static Keccak ToKeccak(Cid value) { return value == null ? null : new Keccak(value.Hash.Digest); } + } +} diff --git a/src/Catalyst.Abstractions/Kvm/IDeltaExecutor.cs b/src/Catalyst.Abstractions/Kvm/IDeltaExecutor.cs index 2fde50c5a9..9c6075a86e 100644 --- a/src/Catalyst.Abstractions/Kvm/IDeltaExecutor.cs +++ b/src/Catalyst.Abstractions/Kvm/IDeltaExecutor.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,7 +29,7 @@ namespace Catalyst.Abstractions.Kvm public interface IDeltaExecutor { void Execute(Delta delta, ITxTracer txTracer); - - void CallAndRestore(Delta delta, ITxTracer txTracer); + + void CallAndReset(Delta delta, ITxTracer txTracer); } } diff --git a/src/Catalyst.Abstractions/Kvm/IDeltaResolver.cs b/src/Catalyst.Abstractions/Kvm/IDeltaResolver.cs index fd8f2842e5..01804456c7 100644 --- a/src/Catalyst.Abstractions/Kvm/IDeltaResolver.cs +++ b/src/Catalyst.Abstractions/Kvm/IDeltaResolver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,19 +21,16 @@ #endregion -using Catalyst.Protocol.Deltas; -using Nethermind.Core.Crypto; +using Lib.P2P; namespace Catalyst.Abstractions.Kvm { public interface IDeltaResolver { - Keccak Resolve(int deltaNumber); + bool TryResolve(long deltaNumber, out Cid deltaHash); - Delta Latest { get; } + long LatestDeltaNumber { get; } - Delta Earliest { get; } - - Delta Pending { get; } + Cid LatestDelta { get; } } } diff --git a/src/Catalyst.Abstractions/Kvm/IKvm.cs b/src/Catalyst.Abstractions/Kvm/IKvm.cs index 937b087563..81a64c5cfc 100644 --- a/src/Catalyst.Abstractions/Kvm/IKvm.cs +++ b/src/Catalyst.Abstractions/Kvm/IKvm.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Kvm/Models/AccountProof.cs b/src/Catalyst.Abstractions/Kvm/Models/AccountProof.cs index 5caa81feee..a1114e4f72 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/AccountProof.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/AccountProof.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Kvm/Models/BlockForRpc.cs b/src/Catalyst.Abstractions/Kvm/Models/BlockForRpc.cs index f4e8ccad11..50c83dd563 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/BlockForRpc.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/BlockForRpc.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,63 +22,19 @@ #endregion using System.Collections.Generic; -using System.Linq; -using System.Numerics; +using Lib.P2P; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Encoding; -using Nethermind.Core.Extensions; -using Nethermind.Core.Json; + using Nethermind.Dirichlet.Numerics; +using Nethermind.Serialization.Json; using Newtonsoft.Json; namespace Catalyst.Abstractions.Kvm.Models { + [JsonObject(ItemNullValueHandling = NullValueHandling.Ignore)] public class BlockForRpc { - private readonly BlockDecoder _blockDecoder = new BlockDecoder(); - - public BlockForRpc() { } - - public BlockForRpc(Block block, bool includeFullTransactionData) - { - var isAuRaBlock = block.Header.AuRaSignature != null; - Author = block.Author; - Difficulty = block.Difficulty; - ExtraData = block.ExtraData; - GasLimit = block.GasLimit; - GasUsed = block.GasUsed; - Hash = block.Hash; - LogsBloom = block.Bloom; - Miner = block.Beneficiary; - if (!isAuRaBlock) - { - MixHash = block.MixHash; - Nonce = ((BigInteger) block.Nonce).ToBigEndianByteArray().PadLeft(8); - } - else - { - Step = block.Header.AuRaStep; - Signature = block.Header.AuRaSignature; - } - - Number = block.Number; - ParentHash = block.ParentHash; - ReceiptsRoot = block.ReceiptsRoot; - Sha3Uncles = block.OmmersHash; - Size = _blockDecoder.GetLength(block, RlpBehaviors.None); - StateRoot = block.StateRoot; - - Timestamp = block.Timestamp; - TotalDifficulty = block.TotalDifficulty ?? 0; - Transactions = includeFullTransactionData - ? block.Transactions.Select((t, idx) => { return new TransactionForRpc(block.Hash, block.Number, idx, t); }).ToArray() - : (object[]) block.Transactions.Select(t => t.Hash).AsEnumerable(); - - TransactionsRoot = block.TransactionsRoot; - Uncles = block.Ommers.Select(o => o.Hash); - } - [JsonConverter(typeof(AddressConverter))] public Address Author { get; set; } @@ -93,9 +49,9 @@ public BlockForRpc(Block block, bool includeFullTransactionData) [JsonConverter(typeof(LongConverter))] public long GasUsed { get; set; } - - [JsonConverter(typeof(KeccakConverter))] - public Keccak Hash { get; set; } + + [JsonConverter(typeof(CidJsonConverter))] + public Cid Hash { get; set; } [JsonConverter(typeof(BloomConverter))] public Bloom LogsBloom { get; set; } @@ -111,9 +67,9 @@ public BlockForRpc(Block block, bool includeFullTransactionData) [JsonConverter(typeof(LongConverter))] public long Number { get; set; } - - [JsonConverter(typeof(KeccakConverter))] - public Keccak ParentHash { get; set; } + + [JsonConverter(typeof(CidJsonConverter))] + public Cid ParentHash { get; set; } [JsonConverter(typeof(KeccakConverter))] public Keccak ReceiptsRoot { get; set; } diff --git a/src/Catalyst.Abstractions/Kvm/Models/BlockParameterType.cs b/src/Catalyst.Abstractions/Kvm/Models/BlockParameterType.cs index 0871a882a5..fa2e1a9c62 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/BlockParameterType.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/BlockParameterType.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,6 +28,7 @@ public enum BlockParameterType Earliest, Latest, Pending, - BlockNumber + BlockNumber, + BlockHash, } } diff --git a/src/Catalyst.Abstractions/Kvm/Models/BlockParameterTypeExtensions.cs b/src/Catalyst.Abstractions/Kvm/Models/BlockParameterTypeExtensions.cs index 14af759f9e..f23f592b42 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/BlockParameterTypeExtensions.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/BlockParameterTypeExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Kvm/Models/BlockParameters.cs b/src/Catalyst.Abstractions/Kvm/Models/BlockParameters.cs index 86a2d33d49..64b197a140 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/BlockParameters.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/BlockParameters.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,63 +22,98 @@ #endregion using System; -using Nethermind.Core.Json; +using Nethermind.Core.Attributes; +using Nethermind.Core.Crypto; +using Nethermind.Serialization.Json; namespace Catalyst.Abstractions.Kvm.Models { - public sealed class BlockParameter + [Todo(Improve.Refactor, "Can make it struct?")] + public class BlockParameter : IEquatable { - private static readonly BlockParameter Earliest = new BlockParameter(BlockParameterType.Earliest); + public static BlockParameter Earliest = new(BlockParameterType.Earliest); - private static readonly BlockParameter Pending = new BlockParameter(BlockParameterType.Pending); + public static BlockParameter Pending = new(BlockParameterType.Pending); - private static readonly BlockParameter Latest = new BlockParameter(BlockParameterType.Latest); + public static BlockParameter Latest = new(BlockParameterType.Latest); public BlockParameterType Type { get; set; } - public long? BlockNumber { get; set; } + public long? BlockNumber { get; } + + public Keccak BlockHash { get; } + + public bool RequireCanonical { get; } public BlockParameter() { } public BlockParameter(BlockParameterType type) { Type = type; - BlockNumber = null; } - public void FromJson(string jsonValue) + public BlockParameter(long number) + { + Type = BlockParameterType.BlockNumber; + BlockNumber = number; + } + + public BlockParameter(Keccak blockHash) + { + Type = BlockParameterType.BlockHash; + BlockHash = blockHash; + } + + public BlockParameter(Keccak blockHash, bool requireCanonical) + { + Type = BlockParameterType.BlockHash; + BlockHash = blockHash; + RequireCanonical = requireCanonical; + } + + public static BlockParameter FromJson(string jsonValue) { switch (jsonValue) { - case string earliest when string.Equals(earliest, "earliest", StringComparison.InvariantCultureIgnoreCase): - Type = BlockParameterType.Earliest; - return; - case string pending when string.Equals(pending, "pending", StringComparison.InvariantCultureIgnoreCase): - Type = BlockParameterType.Pending; - return; - case string latest when string.Equals(latest, "latest", StringComparison.InvariantCultureIgnoreCase): - Type = BlockParameterType.Latest; - return; - case string empty when string.IsNullOrWhiteSpace(empty): - Type = BlockParameterType.Latest; - return; + case { } earliest when string.Equals(earliest, "earliest", StringComparison.InvariantCultureIgnoreCase): + return Earliest; + case { } pending when string.Equals(pending, "pending", StringComparison.InvariantCultureIgnoreCase): + return Pending; + case { } latest when string.Equals(latest, "latest", StringComparison.InvariantCultureIgnoreCase): + return Latest; + case { } empty when string.IsNullOrWhiteSpace(empty): + return Latest; case null: - Type = BlockParameterType.Latest; - return; + return Latest; + case { } hash when hash.Length == 66 && hash.StartsWith("0x"): + return Latest; default: - Type = BlockParameterType.BlockNumber; - BlockNumber = LongConverter.FromString(jsonValue.Trim('"')); - return; + return new BlockParameter(LongConverter.FromString(jsonValue.Trim('"'))); } } public override string ToString() { - return $"{Type}, {BlockNumber}"; + return $"{Type}, {BlockNumber?.ToString() ?? BlockHash?.ToString()}"; + } + + public bool Equals(BlockParameter other) + { + if (ReferenceEquals(null, other)) return false; + if (ReferenceEquals(this, other)) return true; + return Type == other.Type && BlockNumber == other.BlockNumber && BlockHash == other.BlockHash && other.RequireCanonical == RequireCanonical; } - public FilterBlock ToFilterBlock() => - BlockNumber != null - ? new FilterBlock(BlockNumber ?? 0) - : new FilterBlock(Type.ToFilterBlockType()); + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != this.GetType()) return false; + return Equals((BlockParameter) obj); + } + + public override int GetHashCode() + { + throw new NotSupportedException(); + } } } diff --git a/src/Catalyst.Abstractions/Kvm/Models/ErrorType.cs b/src/Catalyst.Abstractions/Kvm/Models/ErrorType.cs index d922915c28..d6f5c3e920 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/ErrorType.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/ErrorType.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Kvm/Models/FilterBlock.cs b/src/Catalyst.Abstractions/Kvm/Models/FilterBlock.cs index dc6e11193c..7a4b60b68a 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/FilterBlock.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/FilterBlock.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Kvm/Models/FilterBlockType.cs b/src/Catalyst.Abstractions/Kvm/Models/FilterBlockType.cs index d71717f04c..1fd93ff65a 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/FilterBlockType.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/FilterBlockType.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,7 +24,7 @@ #region LICENSE // -// Copyright (c) 2019 Catalyst Network +// Copyright (c) 2022 Catalyst Network // // This file is part of Catalyst.Node // diff --git a/src/Catalyst.Abstractions/Kvm/Models/FilterLog.cs b/src/Catalyst.Abstractions/Kvm/Models/FilterLog.cs index 98a3ecd613..0a4dd2281d 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/FilterLog.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/FilterLog.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Kvm/Models/JsonRpcRequest.cs b/src/Catalyst.Abstractions/Kvm/Models/JsonRpcRequest.cs index 907eba07b2..5464eb5348 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/JsonRpcRequest.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/JsonRpcRequest.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -34,16 +34,16 @@ public override void WriteJson(JsonWriter writer, object value, JsonSerializer s switch (value) { case int typedValue: - writer.WriteRawValue(typedValue.ToString()); + writer.WriteValue(typedValue); break; case long typedValue: - writer.WriteRawValue(typedValue.ToString()); + writer.WriteValue(typedValue); break; case BigInteger typedValue: - writer.WriteRawValue(typedValue.ToString()); + writer.WriteValue(typedValue); break; case string typedValue: - writer.WriteRawValue(typedValue); + writer.WriteValue(typedValue); break; default: throw new NotSupportedException(); @@ -75,7 +75,7 @@ public class JsonRpcRequest public string Method { get; set; } [JsonProperty(Required = Required.Default)] - public string[] Params { get; set; } + public object[] Params { get; set; } [JsonConverter(typeof(IdConverter))] public object Id { get; set; } diff --git a/src/Catalyst.Abstractions/Kvm/Models/JsonRpcResponse.cs b/src/Catalyst.Abstractions/Kvm/Models/JsonRpcResponse.cs index 8c2e9b9aba..acebe9d78c 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/JsonRpcResponse.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/JsonRpcResponse.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Kvm/Models/LogEntryForRpc.cs b/src/Catalyst.Abstractions/Kvm/Models/LogEntryForRpc.cs index ce37c11979..e32aad3b2b 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/LogEntryForRpc.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/LogEntryForRpc.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,11 @@ #endregion +using Catalyst.Abstractions.Ledger.Models; +using Lib.P2P; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Json; +using Nethermind.Serialization.Json; using Newtonsoft.Json; namespace Catalyst.Abstractions.Kvm.Models @@ -40,14 +42,14 @@ public LogEntryForRpc(LogEntry logEntry) Topics = logEntry.Topics; } - public LogEntryForRpc(TxReceipt receipt, LogEntry logEntry, int index) + public LogEntryForRpc(TransactionReceipt receipt, LogEntry logEntry, int index) { Removed = false; LogIndex = index; TransactionIndex = receipt.Index; - TransactionHash = receipt.TxHash; - BlockHash = receipt.BlockHash; - BlockNumber = receipt.BlockNumber; + TransactionHash = receipt.DeltaHash; + BlockHash = receipt.DeltaHash; + BlockNumber = receipt.DeltaNumber; Address = logEntry.LoggersAddress; Data = logEntry.Data; Topics = logEntry.Topics; @@ -60,12 +62,12 @@ public LogEntryForRpc(TxReceipt receipt, LogEntry logEntry, int index) [JsonConverter(typeof(NullableLongConverter))] public long? TransactionIndex { get; set; } - - [JsonConverter(typeof(KeccakConverter))] - public Keccak TransactionHash { get; set; } - - [JsonConverter(typeof(KeccakConverter))] - public Keccak BlockHash { get; set; } + + [JsonConverter(typeof(CidJsonConverter))] + public Cid TransactionHash { get; set; } + + [JsonConverter(typeof(CidJsonConverter))] + public Cid BlockHash { get; set; } [JsonConverter(typeof(NullableLongConverter))] public long? BlockNumber { get; set; } diff --git a/src/Catalyst.Abstractions/Kvm/Models/ReceiptForRpc.cs b/src/Catalyst.Abstractions/Kvm/Models/ReceiptForRpc.cs index 81476b9214..901f4556de 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/ReceiptForRpc.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/ReceiptForRpc.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,9 +23,11 @@ using System.Linq; using System.Numerics; +using Catalyst.Abstractions.Ledger.Models; +using Lib.P2P; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Json; +using Nethermind.Serialization.Json; using Newtonsoft.Json; namespace Catalyst.Abstractions.Kvm.Models @@ -34,22 +36,19 @@ public class ReceiptForRpc { public ReceiptForRpc() { } - public ReceiptForRpc(Keccak txHash, TxReceipt receipt) + public ReceiptForRpc(Keccak txHash, TransactionReceipt receipt) { TransactionHash = txHash; TransactionIndex = receipt.Index; - BlockHash = receipt.BlockHash; - BlockNumber = receipt.BlockNumber; + BlockHash = receipt.DeltaHash; + BlockNumber = receipt.DeltaNumber; CumulativeGasUsed = receipt.GasUsedTotal; GasUsed = receipt.GasUsed; - From = receipt.Sender; - To = receipt.Recipient; - ContractAddress = receipt.ContractAddress; + From = new Address(receipt.Sender); + To = new Address(receipt.Recipient); + ContractAddress = new Address(receipt.ContractAddress); Logs = receipt.Logs.Select((l, idx) => new LogEntryForRpc(receipt, l, idx)).ToArray(); - LogsBloom = receipt.Bloom; - Root = receipt.PostTransactionState; Status = receipt.StatusCode; - Error = receipt.Error; } [JsonConverter(typeof(KeccakConverter))] @@ -58,8 +57,8 @@ public ReceiptForRpc(Keccak txHash, TxReceipt receipt) [JsonConverter(typeof(LongConverter))] public long TransactionIndex { get; set; } - [JsonConverter(typeof(KeccakConverter))] - public Keccak BlockHash { get; set; } + [JsonConverter(typeof(CidJsonConverter))] + public Cid BlockHash { get; set; } [JsonConverter(typeof(LongConverter))] public long BlockNumber { get; set; } @@ -80,9 +79,11 @@ public ReceiptForRpc(Keccak txHash, TxReceipt receipt) public Address ContractAddress { get; set; } public LogEntryForRpc[] Logs { get; set; } + + [JsonConverter(typeof(BloomConverter))] public Bloom LogsBloom { get; set; } - public Keccak Root { get; set; } + + [JsonConverter(typeof(BigIntegerConverter))] public BigInteger Status { get; set; } - public string Error { get; set; } } } diff --git a/src/Catalyst.Abstractions/Kvm/Models/ResultWrapper.cs b/src/Catalyst.Abstractions/Kvm/Models/ResultWrapper.cs index 45d6249f81..60d8aacdb1 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/ResultWrapper.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/ResultWrapper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -using Nethermind.Core.Model; +using Nethermind.Core; namespace Catalyst.Abstractions.Kvm.Models { diff --git a/src/Catalyst.Abstractions/Kvm/Models/StorageProof.cs b/src/Catalyst.Abstractions/Kvm/Models/StorageProof.cs index 3733f01fce..be9b6f6adf 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/StorageProof.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/StorageProof.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Kvm/Models/TransactionForRpc.cs b/src/Catalyst.Abstractions/Kvm/Models/TransactionForRpc.cs index 9ce4b1644c..2f91f6bee4 100644 --- a/src/Catalyst.Abstractions/Kvm/Models/TransactionForRpc.cs +++ b/src/Catalyst.Abstractions/Kvm/Models/TransactionForRpc.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,74 +21,60 @@ #endregion -using System.Numerics; +using Lib.P2P; using Nethermind.Core; using Nethermind.Core.Crypto; -using Nethermind.Core.Extensions; using Nethermind.Dirichlet.Numerics; +using Nethermind.Serialization.Json; +using Newtonsoft.Json; namespace Catalyst.Abstractions.Kvm.Models { public class TransactionForRpc { - public TransactionForRpc(Keccak blockHash, BigInteger? blockNumber, int? txIndex, Transaction transaction) - { - Hash = transaction.Hash; - Nonce = transaction.Nonce; - BlockHash = blockHash; - BlockNumber = blockNumber; - TransactionIndex = txIndex; - From = transaction.SenderAddress; - To = transaction.To; - Value = transaction.Value; - GasPrice = transaction.GasPrice; - Gas = transaction.GasLimit; - Input = Data = transaction.Data ?? transaction.Init; - R = transaction.Signature?.R; - S = transaction.Signature?.S; - V = (UInt256?) transaction.Signature?.V; - } - - // ReSharper disable once UnusedMember.Global - public TransactionForRpc() { } - + [JsonConverter(typeof(KeccakConverter))] public Keccak Hash { get; set; } - public BigInteger? Nonce { get; set; } - public Keccak BlockHash { get; set; } - public BigInteger? BlockNumber { get; set; } - public BigInteger? TransactionIndex { get; set; } + + [JsonConverter(typeof(NullableUInt256Converter))] + public UInt256? Nonce { get; set; } + + [JsonConverter(typeof(CidJsonConverter))] + public Cid BlockHash { get; set; } + + [JsonConverter(typeof(NullableUInt256Converter))] + public UInt256? BlockNumber { get; set; } + + [JsonConverter(typeof(NullableUInt256Converter))] + public UInt256? TransactionIndex { get; set; } + + [JsonConverter(typeof(AddressConverter))] public Address From { get; set; } + + [JsonConverter(typeof(AddressConverter))] public Address To { get; set; } - public BigInteger? Value { get; set; } - public BigInteger? GasPrice { get; set; } - public BigInteger? Gas { get; set; } + + [JsonConverter(typeof(NullableUInt256Converter))] + public UInt256? Value { get; set; } + + [JsonConverter(typeof(NullableUInt256Converter))] + public UInt256? GasPrice { get; set; } + + [JsonConverter(typeof(NullableUInt256Converter))] + public UInt256? Gas { get; set; } + + [JsonConverter(typeof(ByteArrayConverter))] public byte[] Data { get; set; } + + [JsonConverter(typeof(ByteArrayConverter))] public byte[] Input { get; set; } + + [JsonConverter(typeof(NullableUInt256Converter))] public UInt256? V { get; set; } + [JsonConverter(typeof(ByteArrayConverter))] public byte[] S { get; set; } + [JsonConverter(typeof(ByteArrayConverter))] public byte[] R { get; set; } - - public Transaction ToTransaction() - { - Transaction tx = new Transaction(); - tx.GasLimit = (long) (Gas ?? 90000); - tx.GasPrice = (UInt256) (GasPrice ?? 20.GWei()); - tx.Nonce = (ulong) (Nonce ?? 0); // here pick the last nonce? - tx.To = To; - tx.SenderAddress = From; - tx.Value = (UInt256) (Value ?? 0); - if (tx.To == null) - { - tx.Init = Data ?? Input; - } - else - { - tx.Data = Data ?? Input; - } - - return tx; - } } } diff --git a/src/Catalyst.Abstractions/Ledger/ILedger.cs b/src/Catalyst.Abstractions/Ledger/ILedger.cs index 9acd793def..f6e064a776 100644 --- a/src/Catalyst.Abstractions/Ledger/ILedger.cs +++ b/src/Catalyst.Abstractions/Ledger/ILedger.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,7 @@ #endregion using Catalyst.Abstractions.Ledger.Models; -using LibP2P; +using Lib.P2P; namespace Catalyst.Abstractions.Ledger { @@ -61,5 +61,10 @@ public interface ILedger /// A boolean indicating whether a synchronisation of the ledger is in currently in process. /// bool IsSynchonising { get; } + + /// + /// The latest know delta number. + /// + long LatestKnownDeltaNumber { get; } } } diff --git a/src/Catalyst.Abstractions/Ledger/IWeb3EthApi.cs b/src/Catalyst.Abstractions/Ledger/IWeb3EthApi.cs index aa154c93c6..d04f8953c8 100644 --- a/src/Catalyst.Abstractions/Ledger/IWeb3EthApi.cs +++ b/src/Catalyst.Abstractions/Ledger/IWeb3EthApi.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,16 +21,40 @@ #endregion +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.Kvm; -using Nethermind.Store; +using Catalyst.Abstractions.Ledger.Models; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Core.Abstractions.Sync; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Transaction; +using Lib.P2P; +using Nethermind.Core.Crypto; +using Nethermind.State; +using System.Collections.Generic; namespace Catalyst.Abstractions.Ledger { public interface IWeb3EthApi { - IDeltaExecutor DeltaExecutor { get; } IStateReader StateReader { get; } IDeltaResolver DeltaResolver { get; } - IStateRootResolver StateRootResolver { get; } + IDeltaCache DeltaCache { get; } + + IDeltaExecutor Executor { get; } + IStorageProvider StorageProvider { get; } + IStateProvider StateProvider { get; } + IHashProvider HashProvider { get; } + IPeerRepository PeerRepository { get; } + IDfsService DfsService { get; } + SyncState SyncState { get; } + + Keccak SendTransaction(PublicEntry publicEntry); + + TransactionReceipt FindReceipt(Keccak transactionHash); + bool FindTransactionData(Keccak transactionHash, out Cid deltaHash, out Delta delta, out int index); + IEnumerable GetPendingTransactions(); } } diff --git a/src/Catalyst.Abstractions/Ledger/Models/Account.cs b/src/Catalyst.Abstractions/Ledger/Models/Account.cs index 5e80cddee9..1af5bfe508 100644 --- a/src/Catalyst.Abstractions/Ledger/Models/Account.cs +++ b/src/Catalyst.Abstractions/Ledger/Models/Account.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -59,7 +59,7 @@ public Account(string publicAddress, [RepositoryPrimaryKey(Order = 1)] [JsonProperty("id")] - public string DocumentId => + public string Id => ByteString.CopyFrom(Encoding.UTF8.GetBytes($"{PublicAddress}-{AccountType?.Name}"))?.ToBase64(); } } diff --git a/src/Catalyst.Abstractions/Ledger/Models/DeltaByNumber.cs b/src/Catalyst.Abstractions/Ledger/Models/DeltaByNumber.cs new file mode 100644 index 0000000000..56aea4f31e --- /dev/null +++ b/src/Catalyst.Abstractions/Ledger/Models/DeltaByNumber.cs @@ -0,0 +1,58 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Repository; +using Lib.P2P; +using Newtonsoft.Json; +using SharpRepository.Repository; + +namespace Catalyst.Abstractions.Ledger.Models +{ + /// + /// Provides a mapping between the delta number and its . + /// + public class DeltaByNumber : IDocument + { + public DeltaByNumber() + { + // this constructor only exists to allow SharpRepository + // to store instances of this class + } + + public DeltaByNumber(long number, Cid deltaId) + { + Number = number; + DeltaId = deltaId; + } + + public long Number { get; } + + public Cid DeltaId { get; } + + [RepositoryPrimaryKey(Order = 1)] + [JsonProperty("id")] + public string DocumentId => BuildDocumentId(Number); + + public static string BuildDocumentId(long number) => number.ToString("D"); + } +} diff --git a/src/Catalyst.Abstractions/Ledger/Models/IAccount.cs b/src/Catalyst.Abstractions/Ledger/Models/IAccount.cs index 7b10987cc3..afe4431cc1 100644 --- a/src/Catalyst.Abstractions/Ledger/Models/IAccount.cs +++ b/src/Catalyst.Abstractions/Ledger/Models/IAccount.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -31,7 +31,7 @@ namespace Catalyst.Abstractions.Ledger.Models /// This class represent a user account of which there can be the following types: /// confidential account, non-confidential account and smart contract account /// - public interface IAccount : IDocument + public interface IAccount { /// /// The address used to identify the account. diff --git a/src/Catalyst.Abstractions/Ledger/Models/TransactionReceipt.cs b/src/Catalyst.Abstractions/Ledger/Models/TransactionReceipt.cs new file mode 100644 index 0000000000..f12aa17ab8 --- /dev/null +++ b/src/Catalyst.Abstractions/Ledger/Models/TransactionReceipt.cs @@ -0,0 +1,65 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Lib.P2P; +using Nethermind.Core; +using Newtonsoft.Json; +using SharpRepository.Repository; +using System.ComponentModel.DataAnnotations; + +namespace Catalyst.Abstractions.Ledger.Models +{ + public class TransactionReceipts + { + public TransactionReceipt[] Receipts { get; set; } + + [RepositoryPrimaryKey(Order = 1)] + [Key] + [JsonProperty("id")] + public string Id { get; set; } + } + + public class TransactionReceipt + { + public long Index { get; set; } + public Cid DeltaHash { get; set; } + public long DeltaNumber { get; set; } + public long GasUsedTotal { get; set; } + public long GasUsed { get; set; } + public string Sender { get; set; } + public string Recipient { get; set; } + public string ContractAddress { get; set; } + public byte StatusCode { get; set; } + public LogEntry[] Logs { get; set; } + } + + public class TransactionToDelta + { + [RepositoryPrimaryKey(Order = 1)] + [Key] + [JsonProperty("id")] + public string Id { get; set; } + + public Cid DeltaHash { get; set; } + } +} diff --git a/src/Catalyst.Abstractions/Mempool/IMempool.cs b/src/Catalyst.Abstractions/Mempool/IMempool.cs index d086b1cfa8..aec687375f 100644 --- a/src/Catalyst.Abstractions/Mempool/IMempool.cs +++ b/src/Catalyst.Abstractions/Mempool/IMempool.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,12 +21,12 @@ #endregion -using Catalyst.Abstractions.Mempool.Repositories; +using Catalyst.Abstractions.Mempool.Services; namespace Catalyst.Abstractions.Mempool { public interface IMempool where T : class { - IMempoolRepository Repository { get; } + IMempoolService Service { get; } } } diff --git a/src/Catalyst.Abstractions/Mempool/Services/IMempoolService.cs b/src/Catalyst.Abstractions/Mempool/Services/IMempoolService.cs new file mode 100644 index 0000000000..2391d34ba7 --- /dev/null +++ b/src/Catalyst.Abstractions/Mempool/Services/IMempoolService.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; + +namespace Catalyst.Abstractions.Mempool.Services +{ + public interface IMempoolService : IDisposable where T : class + { + IEnumerable GetAll(); + + void Delete(IEnumerable mempoolItem); + + bool TryReadItem(string id); + + T ReadItem(string id); + + bool DeleteItem(params string[] ids); + + bool CreateItem(T mempoolItem); + } +} diff --git a/src/Catalyst.Abstractions/Network/IDns.cs b/src/Catalyst.Abstractions/Network/IDns.cs index 5cec7f68fa..076ced31f4 100644 --- a/src/Catalyst.Abstractions/Network/IDns.cs +++ b/src/Catalyst.Abstractions/Network/IDns.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,6 +25,7 @@ using System.Threading.Tasks; using Catalyst.Protocol.Peer; using DnsClient; +using MultiFormats; namespace Catalyst.Abstractions.Network { @@ -47,6 +48,6 @@ public interface IDns /// /// /// - Task> GetSeedNodesFromDnsAsync(IEnumerable urls); + Task> GetSeedNodesFromDnsAsync(IEnumerable urls); } } diff --git a/src/Catalyst.Abstractions/Network/InjectableLookupClient.cs b/src/Catalyst.Abstractions/Network/InjectableLookupClient.cs index b3d39ff1e4..729a61130a 100644 --- a/src/Catalyst.Abstractions/Network/InjectableLookupClient.cs +++ b/src/Catalyst.Abstractions/Network/InjectableLookupClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Options/AddFileOptions.cs b/src/Catalyst.Abstractions/Options/AddFileOptions.cs new file mode 100644 index 0000000000..2d90e11a71 --- /dev/null +++ b/src/Catalyst.Abstractions/Options/AddFileOptions.cs @@ -0,0 +1,126 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.Dfs.CoreApi; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Abstractions.Options +{ + /// + /// The options when adding data to the IPFS file system. + /// + /// + public class AddFileOptions + { + /// + /// Determines if the data is pinned to local storage. + /// + /// + /// If true the data is pinned to local storage and will not be + /// garbage collected. The default is true. + /// + public bool Pin { get; set; } = true; + + /// + /// The maximum number of data bytes in a block. + /// + /// + /// The default is 256 * 1024 (‭262,144) bytes.‬ + /// + public int ChunkSize { get; set; } = 256 * 1024; + + /// + /// Determines if the trickle-dag format is used for dag generation. + /// + /// + /// The default is false. + /// + public bool Trickle { get; set; } = false; + + /// + /// Determines if added file(s) are wrapped in a directory object. + /// + /// + /// The default is false. + /// + public bool Wrap { get; set; } = false; + + /// + /// Determines if raw blocks are used for leaf data blocks. + /// + /// + /// The default is false. + /// + /// + /// RawLeaves and are mutually exclusive. + /// + public bool RawLeaves { get; set; } = false; + + /// + /// The hashing algorithm name to use. + /// + /// + /// The algorithm name used to produce the . + /// Defaults to . + /// + /// + public string Hash { get; set; } = MultiHash.DefaultAlgorithmName; + + /// + /// The encoding algorithm name to use. + /// + /// + /// The algorithm name used to produce the . + /// Defaults to . + /// + /// + public string Encoding { get; set; } = MultiBase.DefaultAlgorithmName; + + /// + /// Determines if only file information is produced. + /// + /// + /// If true no data is added to IPFS. The default is false. + /// + public bool OnlyHash { get; set; } = false; + + /// + /// The key name used to protect (encrypt) the file contents. + /// + /// + /// The name of an existing key. + /// + /// + /// ProtectionKey and are mutually exclusive. + /// + /// + public string ProtectionKey { get; set; } + + /// + /// Used to report the progress of a file transfer. + /// + public IProgress Progress { get; set; } + } +} diff --git a/src/Catalyst.Abstractions/Options/BlockOptions.cs b/src/Catalyst.Abstractions/Options/BlockOptions.cs new file mode 100644 index 0000000000..ee9e826db6 --- /dev/null +++ b/src/Catalyst.Abstractions/Options/BlockOptions.cs @@ -0,0 +1,63 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Dfs.CoreApi; + +namespace Catalyst.Abstractions.Options +{ + /// + /// Configuration options for a block service. + /// + /// + public class BlockOptions + { + /// + /// Determines if an inline CID can be created. + /// + /// + /// Defaults to false. + /// + /// + /// An "inline CID" places the content in the CID not in a seperate block. + /// It is used to speed up access to content that is small. + /// + public bool AllowInlineCid { get; set; } + + /// + /// Used to determine if the content is small enough to be inlined. + /// + /// + /// The maximum number of bytes for content that will be inlined. + /// Defaults to 64. + /// + public int InlineCidLimit { get; set; } = 64; + + /// + /// The maximun length of data block. + /// + /// + /// + /// 1MB (1024 * 1024) + public int MaxBlockSize { get; } = 1024 * 1024; + } +} diff --git a/src/Catalyst.Abstractions/Options/DfsOptions.cs b/src/Catalyst.Abstractions/Options/DfsOptions.cs new file mode 100644 index 0000000000..e255764836 --- /dev/null +++ b/src/Catalyst.Abstractions/Options/DfsOptions.cs @@ -0,0 +1,103 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using Lib.P2P.Cryptography; +using Makaretu.Dns; +using MultiFormats; + +namespace Catalyst.Abstractions.Options +{ + /// + /// Configuration options for the . + /// + /// + public class DfsOptions + { + /// + /// Repository options. + /// + public RepositoryOptions Repository { get; set; } + + /// + /// KeyChain options. + /// + public KeyChainOptions KeyChain { get; set; } + + /// + /// Provides access to the Domain Name System. + /// + /// + /// Defaults to , DNS over TLS. + /// + public IDnsClient Dns { get; set; } + + /// + /// Block options. + /// + public BlockOptions Block { get; set; } + + /// + /// Discovery options. + /// + public DiscoveryOptions Discovery { get; set; } + + /// + /// Swarm (network) options. + /// + public SwarmOptions Swarm { get; set; } + + public DfsOptions(BlockOptions blockOptions, + DiscoveryOptions discoveryOptions, + RepositoryOptions repositoryOptions, + KeyChainOptions keyChainOptions, + SwarmOptions swarmOptions, + IDnsClient dnsClient) + { + Block = blockOptions; + Discovery = discoveryOptions; + Repository = repositoryOptions; + KeyChain = keyChainOptions; + Swarm = swarmOptions; + Dns = dnsClient; + + // Do not use the public IPFS network, use a private network of catalyst only nodes. + var swarmKey = "07a8e9d0c43400927ab274b7fa443596b71e609bacae47bd958e5cd9f59d6ca3"; + Swarm.PrivateNetworkKey = new PreSharedKey + { + Value = swarmKey.ToHexBuffer() + }; + + if (Swarm.PrivateNetworkKey == null) + { + var path = Path.Combine(Repository.Folder, "swarm.key"); + if (File.Exists(path)) + { + using var x = File.OpenText(path); + Swarm.PrivateNetworkKey = new PreSharedKey(); + Swarm.PrivateNetworkKey.Import(x); + } + } + } + } +} diff --git a/src/Catalyst.Abstractions/Options/DiscoveryOptions.cs b/src/Catalyst.Abstractions/Options/DiscoveryOptions.cs new file mode 100644 index 0000000000..b38be757c1 --- /dev/null +++ b/src/Catalyst.Abstractions/Options/DiscoveryOptions.cs @@ -0,0 +1,65 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using MultiFormats; + +namespace Catalyst.Abstractions.Options +{ + /// + /// Configuration options for discovering other peers. + /// + /// + public class DiscoveryOptions + { + /// + /// Well known peers used to find other peers in + /// the IPFS network. + /// + /// + /// The default value is null. + /// + /// + /// If not null, then the sequence is use by + /// the block API; otherwise the values in the configuration + /// file are used. + /// + public IEnumerable BootstrapPeers { set; get; } + + /// + /// Disables the multicast DNS discovery of other peers + /// and advertising of this peer. + /// + public bool DisableMdns { set; get; } + + public bool UsePeerRepository { set; get; } + + /// + /// Disables discovery of other peers by walking the + /// DHT. + /// + public bool DisableRandomWalk { set; get; } + + public DiscoveryOptions() { } + } +} diff --git a/src/Catalyst.Abstractions/Options/KeyChainOptions.cs b/src/Catalyst.Abstractions/Options/KeyChainOptions.cs new file mode 100644 index 0000000000..c28d8910db --- /dev/null +++ b/src/Catalyst.Abstractions/Options/KeyChainOptions.cs @@ -0,0 +1,97 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.Options +{ + /// + /// Configuration options for the . + /// + /// + public class KeyChainOptions + { + /// + /// The default key type, when generating a key. + /// + /// + /// "rsa", "ed25519" or "secp256k1". Defaults to "rsa". + /// + public string DefaultKeyType { get; set; } = "ed25519"; + + /// + /// The default key size, when generating a RSA key. + /// + /// + /// The size in bits. Defaults to 2048. + /// + public int DefaultKeySize { get; set; } = 2048; + + /// + /// The defaults for the derived encryption key. + /// + /// + /// The options to generated a DEK. + /// + /// + /// The derived encryption key is used to store the encrypted keys. + /// Theses options can not change once the key chain is created. + /// + public KeyChainDekOptions Dek { get; set; } = new(); + } + + /// + /// Options to generate the derived encryption key. + /// + /// + public class KeyChainDekOptions + { + /// + /// The desired length of the derived key + /// + /// + /// The length in bytes. Defaults to 512. + /// + public int KeyLength { get; set; } = 512 / 8; + + /// + /// The number of iterations desired + /// + /// + /// Defaults to 10,000. + /// + public int IterationCount { get; set; } = 10 * 1000; + + /// + /// Some random data for the . + /// + /// + public string Salt { get; set; } = "at least 16 characters long"; + + /// + /// The name of the hashing function. + /// + /// + /// One of the known names. Defaults to "sha2-512". + /// + public string Hash { get; set; } = "sha2-512"; + } +} diff --git a/src/Catalyst.Abstractions/Options/RepositoryOptions.cs b/src/Catalyst.Abstractions/Options/RepositoryOptions.cs new file mode 100644 index 0000000000..668c8a717f --- /dev/null +++ b/src/Catalyst.Abstractions/Options/RepositoryOptions.cs @@ -0,0 +1,92 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using Catalyst.Abstractions.FileSystem; + +namespace Catalyst.Abstractions.Options +{ + /// + /// Configuration options for the repository. + /// + /// + public class RepositoryOptions + { + /// + /// Creates a new instance of the class + /// with the default values. + /// + public RepositoryOptions(IFileSystem fileSystem = default, string dfsDirectory = default) + { + var path = Environment.GetEnvironmentVariable("IPFS_PATH"); + if (path != null) + { + Folder = path; + } + else + { + Folder = Path.Combine( + Environment.GetEnvironmentVariable("HOME") ?? + Environment.GetEnvironmentVariable("HOMEPATH"), + ".catalyst"); + } + + if (fileSystem != default && dfsDirectory != default) + { + Folder = new DirectoryInfo(Path.Combine(fileSystem.GetCatalystDataDir().FullName, dfsDirectory)) + .FullName; + Directory.CreateDirectory(Folder); + } + } + + /// + /// The directory of the repository. + /// + /// + /// The default value is $IPFS_PATH or $HOME/.csipfs or + /// $HOMEPATH/.csipfs. + /// + public string Folder { get; set; } + + /// + /// Get the existing directory of the repository. + /// + /// + /// An existing directory. + /// + /// + /// Creates the if it does not exist. + /// + public string ExistingFolder() + { + var path = Folder; + if (!Directory.Exists(path)) + { + Directory.CreateDirectory(path); + } + + return path; + } + } +} diff --git a/src/Catalyst.Abstractions/Options/SwarmOptions.cs b/src/Catalyst.Abstractions/Options/SwarmOptions.cs new file mode 100644 index 0000000000..949ab8aa60 --- /dev/null +++ b/src/Catalyst.Abstractions/Options/SwarmOptions.cs @@ -0,0 +1,68 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Lib.P2P; +using Lib.P2P.Cryptography; + +namespace Catalyst.Abstractions.Options +{ + /// + /// Configuration options for communication with other peers. + /// + /// + public class SwarmOptions + { + /// + /// The key of the private network. + /// + /// + /// The key must either null or 32 bytes (256 bits) in length. + /// + /// + /// When null, the public network is used. Otherwise, the network is + /// considered private and only peers with the same key will be + /// communicated with. + /// + /// When using a private network, the + /// must also use this key. + /// + /// + /// + public PreSharedKey PrivateNetworkKey { get; set; } + + /// + /// The low water mark for peer connections. + /// + /// + /// Defaults to 0. + /// + /// + /// The is used to maintain at + /// least this number of connections. + /// + /// This is an opt-feature. The value must be positive to enable it. + /// + /// + public int MinConnections { get; set; } = 8; + } +} diff --git a/src/Catalyst.Abstractions/P2P/Discovery/BaseDiscovery.cs b/src/Catalyst.Abstractions/P2P/Discovery/BaseDiscovery.cs index c1563e18b8..537e446c66 100644 --- a/src/Catalyst.Abstractions/P2P/Discovery/BaseDiscovery.cs +++ b/src/Catalyst.Abstractions/P2P/Discovery/BaseDiscovery.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/P2P/Discovery/IHastingsCareTaker.cs b/src/Catalyst.Abstractions/P2P/Discovery/IHastingsCareTaker.cs index 0398a96bc4..c9eb4a8d4f 100644 --- a/src/Catalyst.Abstractions/P2P/Discovery/IHastingsCareTaker.cs +++ b/src/Catalyst.Abstractions/P2P/Discovery/IHastingsCareTaker.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/P2P/Discovery/IHastingsMemento.cs b/src/Catalyst.Abstractions/P2P/Discovery/IHastingsMemento.cs index 23863d3564..c6fa1ef84f 100644 --- a/src/Catalyst.Abstractions/P2P/Discovery/IHastingsMemento.cs +++ b/src/Catalyst.Abstractions/P2P/Discovery/IHastingsMemento.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,6 +23,7 @@ using Catalyst.Protocol.IPPN; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Abstractions.P2P.Discovery { @@ -37,7 +38,7 @@ public interface IHastingsMemento /// /// The peer identifier of the node used to discover new nodes. /// - PeerId Peer { get; } + MultiAddress Peer { get; } /// /// A list of neighbours, provided by through a diff --git a/src/Catalyst.Abstractions/P2P/Discovery/IHastingsOriginator.cs b/src/Catalyst.Abstractions/P2P/Discovery/IHastingsOriginator.cs index 7359bcdb65..eb4627b3e3 100644 --- a/src/Catalyst.Abstractions/P2P/Discovery/IHastingsOriginator.cs +++ b/src/Catalyst.Abstractions/P2P/Discovery/IHastingsOriginator.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,12 +25,13 @@ using Catalyst.Abstractions.Types; using Catalyst.Protocol.IPPN; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Abstractions.P2P.Discovery { public interface IHastingsOriginator { - PeerId Peer { get; } + MultiAddress Peer { get; } /// /// Every time you the walk moves forward with a new Peer, it will ask that peer for diff --git a/src/Catalyst.Abstractions/P2P/Discovery/IHealthChecker.cs b/src/Catalyst.Abstractions/P2P/Discovery/IHealthChecker.cs index ee1d7789c1..2c71a9f2ac 100644 --- a/src/Catalyst.Abstractions/P2P/Discovery/IHealthChecker.cs +++ b/src/Catalyst.Abstractions/P2P/Discovery/IHealthChecker.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/P2P/Discovery/INeighbour.cs b/src/Catalyst.Abstractions/P2P/Discovery/INeighbour.cs index 8c2232bc5a..738b871349 100644 --- a/src/Catalyst.Abstractions/P2P/Discovery/INeighbour.cs +++ b/src/Catalyst.Abstractions/P2P/Discovery/INeighbour.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,13 +24,14 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.Types; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Abstractions.P2P.Discovery { public interface INeighbour { NeighbourStateTypes StateTypes { get; set; } - PeerId PeerId { get; } + MultiAddress Address { get; } ICorrelationId DiscoveryPingCorrelationId { get; } } } diff --git a/src/Catalyst.Abstractions/P2P/Discovery/INeighbours.cs b/src/Catalyst.Abstractions/P2P/Discovery/INeighbours.cs index f6302dd7cd..3667565448 100644 --- a/src/Catalyst.Abstractions/P2P/Discovery/INeighbours.cs +++ b/src/Catalyst.Abstractions/P2P/Discovery/INeighbours.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/P2P/Discovery/IPeerDiscovery.cs b/src/Catalyst.Abstractions/P2P/Discovery/IPeerDiscovery.cs index f4260a2e97..4ed3b423a0 100644 --- a/src/Catalyst.Abstractions/P2P/Discovery/IPeerDiscovery.cs +++ b/src/Catalyst.Abstractions/P2P/Discovery/IPeerDiscovery.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/P2P/IO/IPeerClientObservable.cs b/src/Catalyst.Abstractions/P2P/IO/IPeerClientObservable.cs index 4645d7f558..ec0c8f7930 100644 --- a/src/Catalyst.Abstractions/P2P/IO/IPeerClientObservable.cs +++ b/src/Catalyst.Abstractions/P2P/IO/IPeerClientObservable.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,11 +21,11 @@ #endregion +using Catalyst.Abstractions.P2P.IO.Messaging.Dto; using System; using System.Reactive.Subjects; -using Catalyst.Abstractions.P2P.IO.Messaging.Dto; -namespace Catalyst.Abstractions.P2P.IO +namespace Catalyst.Core.Lib.Abstractions.P2P.IO { public interface IPeerClientObservable { diff --git a/src/Catalyst.Abstractions/P2P/IO/Messaging/Correlation/IPeerMessageCorrelationManager.cs b/src/Catalyst.Abstractions/P2P/IO/Messaging/Correlation/IPeerMessageCorrelationManager.cs index 67e265ceda..124eb1687f 100644 --- a/src/Catalyst.Abstractions/P2P/IO/Messaging/Correlation/IPeerMessageCorrelationManager.cs +++ b/src/Catalyst.Abstractions/P2P/IO/Messaging/Correlation/IPeerMessageCorrelationManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,12 +26,13 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.P2P.ReputationSystem; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Abstractions.P2P.IO.Messaging.Correlation { public interface IPeerMessageCorrelationManager : IMessageCorrelationManager { IObservable ReputationEventStream { get; } - IObservable> EvictionEventStream { get; } + IObservable> EvictionEventStream { get; } } } diff --git a/src/Catalyst.Abstractions/P2P/IO/Messaging/Dto/IPeerClientMessageDto.cs b/src/Catalyst.Abstractions/P2P/IO/Messaging/Dto/IPeerClientMessageDto.cs index 509199a605..7538e09e60 100644 --- a/src/Catalyst.Abstractions/P2P/IO/Messaging/Dto/IPeerClientMessageDto.cs +++ b/src/Catalyst.Abstractions/P2P/IO/Messaging/Dto/IPeerClientMessageDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,6 +24,7 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Protocol.Peer; using Google.Protobuf; +using MultiFormats; namespace Catalyst.Abstractions.P2P.IO.Messaging.Dto { @@ -33,7 +34,7 @@ namespace Catalyst.Abstractions.P2P.IO.Messaging.Dto public interface IPeerClientMessageDto { ICorrelationId CorrelationId { get; set; } - PeerId Sender { get; set; } + MultiAddress Sender { get; set; } IMessage Message { get; set; } } } diff --git a/src/Catalyst.Abstractions/P2P/IPeerClient.cs b/src/Catalyst.Abstractions/P2P/IPeerClient.cs index 8fffac86b6..819cfa0be8 100644 --- a/src/Catalyst.Abstractions/P2P/IPeerClient.cs +++ b/src/Catalyst.Abstractions/P2P/IPeerClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,22 @@ #endregion -using Catalyst.Abstractions.IO.Transport; +using Catalyst.Protocol.Wire; +using Google.Protobuf; +using MultiFormats; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; namespace Catalyst.Abstractions.P2P { - public interface IPeerClient : ISocketClient { } + public interface IPeerClient : IDisposable + { + Task SendMessageToPeersAsync(IMessage message, IEnumerable peers); + Task SendMessageAsync(ProtocolMessage message, MultiAddress recipient); + Task BroadcastAsync(ProtocolMessage message); + Task StartAsync(); + Task StartAsync(CancellationToken cancellationToken); + } } diff --git a/src/Catalyst.Abstractions/P2P/IPeerIdValidator.cs b/src/Catalyst.Abstractions/P2P/IPeerIdValidator.cs index 053dc1160f..d0b84f7805 100644 --- a/src/Catalyst.Abstractions/P2P/IPeerIdValidator.cs +++ b/src/Catalyst.Abstractions/P2P/IPeerIdValidator.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,6 +22,7 @@ #endregion using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Abstractions.P2P { @@ -33,6 +34,6 @@ public interface IPeerIdValidator /// Validates the peer. /// The Peer Id /// [true] if valid [false] if invalid - bool ValidatePeerIdFormat(PeerId peerId); + bool ValidatePeerIdFormat(MultiAddress Address); } } diff --git a/src/Catalyst.Abstractions/P2P/IPeerService.cs b/src/Catalyst.Abstractions/P2P/IPeerService.cs index 8fd2d65053..ba4a5a7309 100644 --- a/src/Catalyst.Abstractions/P2P/IPeerService.cs +++ b/src/Catalyst.Abstractions/P2P/IPeerService.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,18 +21,17 @@ #endregion -using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.IO.Transport; -using Catalyst.Abstractions.P2P.Discovery; using Catalyst.Protocol.Wire; +using System; +using System.Threading; +using System.Threading.Tasks; namespace Catalyst.Abstractions.P2P { - public interface IPeerService : IObservableMessageStreamer, ISocket + public interface IPeerService : IDisposable { - /// - /// The discovery mechanism for the peer network. - /// - IPeerDiscovery Discovery { get; } + IObservable MessageStream { get; } + Task StartAsync(); + Task StartAsync(CancellationToken cancellationToken); } } diff --git a/src/Catalyst.Abstractions/P2P/IPeerSettings.cs b/src/Catalyst.Abstractions/P2P/IPeerSettings.cs index 639a3a3df2..268bcee943 100644 --- a/src/Catalyst.Abstractions/P2P/IPeerSettings.cs +++ b/src/Catalyst.Abstractions/P2P/IPeerSettings.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,6 +25,7 @@ using System.Net; using Catalyst.Protocol.Network; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Abstractions.P2P { @@ -37,7 +38,7 @@ public interface IPeerSettings IList SeedServers { get; } NetworkType NetworkType { get; } IPEndPoint[] DnsServers { get; } - PeerId PeerId { get; } + MultiAddress Address { get; } } } diff --git a/src/Catalyst.Abstractions/P2P/Models/IPeer.cs b/src/Catalyst.Abstractions/P2P/Models/IPeer.cs index ab08e57971..9766076a9a 100644 --- a/src/Catalyst.Abstractions/P2P/Models/IPeer.cs +++ b/src/Catalyst.Abstractions/P2P/Models/IPeer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,7 +24,8 @@ using System; using Catalyst.Abstractions.Attributes; using Catalyst.Abstractions.Repository; -using Catalyst.Protocol.Peer; +using MultiFormats; +using Nethermind.Core; namespace Catalyst.Abstractions.P2P.Models { @@ -44,7 +45,11 @@ public interface IPeer : IDocument, IAuditable /// Gets the peer identifier. /// The peer identifier. - PeerId PeerId { get; } + MultiAddress Address { get; } + + /// Gets the kvm address. + /// The kvm address. + Address KvmAddress { set; get; } /// Gets a value indicating whether this instance is awol peer. /// true if this instance is awol peer; otherwise, false. diff --git a/src/Catalyst.Core.Lib/P2P/Models/Peer.cs b/src/Catalyst.Abstractions/P2P/Models/Peer.cs similarity index 80% rename from src/Catalyst.Core.Lib/P2P/Models/Peer.cs rename to src/Catalyst.Abstractions/P2P/Models/Peer.cs index d97337fefb..7ad9350c17 100644 --- a/src/Catalyst.Core.Lib/P2P/Models/Peer.cs +++ b/src/Catalyst.Abstractions/P2P/Models/Peer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,11 +22,11 @@ #endregion using System; +using Catalyst.Abstractions.Lib.Util; using Catalyst.Abstractions.P2P.Models; -using Catalyst.Core.Lib.Repository.Attributes; -using Catalyst.Core.Lib.Util; -using Catalyst.Protocol.Peer; -using Google.Protobuf; +using Catalyst.Abstractions.Service.Attributes; +using MultiFormats; +using Nethermind.Core; using SharpRepository.Repository; namespace Catalyst.Core.Lib.P2P.Models @@ -35,11 +35,17 @@ namespace Catalyst.Core.Lib.P2P.Models public sealed class Peer : IPeer { [RepositoryPrimaryKey(Order = 1)] - public string DocumentId => PeerId?.ToByteString().ToBase64(); - + public string DocumentId => Address.ToString(); + + /// + public MultiAddress Address { get; set; } + /// - public PeerId PeerId { get; set; } - + public Address KvmAddress { get; set; } + + /// + public bool IsPoaNode { set; get; } + /// public int Reputation { get; set; } diff --git a/src/Catalyst.Abstractions/P2P/IPeerChallenger.cs b/src/Catalyst.Abstractions/P2P/Protocols/IPeerChallengeRequest.cs similarity index 84% rename from src/Catalyst.Abstractions/P2P/IPeerChallenger.cs rename to src/Catalyst.Abstractions/P2P/Protocols/IPeerChallengeRequest.cs index 66b6cc31ec..32f493aac3 100644 --- a/src/Catalyst.Abstractions/P2P/IPeerChallenger.cs +++ b/src/Catalyst.Abstractions/P2P/Protocols/IPeerChallengeRequest.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,16 +21,18 @@ #endregion +using System; using System.Reactive.Subjects; using System.Threading.Tasks; using Catalyst.Protocol.Peer; +using MultiFormats; -namespace Catalyst.Abstractions.P2P +namespace Catalyst.Abstractions.P2P.Protocols { /// /// This class is used to validate peers by carrying out a peer challenge response /// - public interface IPeerChallenger + public interface IPeerChallengeRequest : IProtocolRequest, IDisposable { /// /// Used to challenge a peer for a response based on the provided public key, ip and port chunks @@ -38,7 +40,7 @@ public interface IPeerChallenger /// The recipient peer identifier. /// PeerId holds the chunks we want to validate. /// bool true means valid and false means not valid - Task ChallengePeerAsync(PeerId recipientPeerIdentifier); + Task ChallengePeerAsync(MultiAddress recipientPeerIdentifier); ReplaySubject ChallengeResponseMessageStreamer { get; } } diff --git a/src/Catalyst.Abstractions/P2P/IPeerChallengeResponse.cs b/src/Catalyst.Abstractions/P2P/Protocols/IPeerChallengeResponse.cs similarity index 85% rename from src/Catalyst.Abstractions/P2P/IPeerChallengeResponse.cs rename to src/Catalyst.Abstractions/P2P/Protocols/IPeerChallengeResponse.cs index 023c05485a..ef71183550 100644 --- a/src/Catalyst.Abstractions/P2P/IPeerChallengeResponse.cs +++ b/src/Catalyst.Abstractions/P2P/Protocols/IPeerChallengeResponse.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,11 +22,12 @@ #endregion using Catalyst.Protocol.Peer; +using MultiFormats; -namespace Catalyst.Abstractions.P2P +namespace Catalyst.Abstractions.P2P.Protocols { public interface IPeerChallengeResponse { - PeerId PeerId { get; } + MultiAddress Address { get; } } } diff --git a/src/Catalyst.Abstractions/P2P/Protocols/IPeerQueryTipRequest.cs b/src/Catalyst.Abstractions/P2P/Protocols/IPeerQueryTipRequest.cs new file mode 100644 index 0000000000..073c4e5a68 --- /dev/null +++ b/src/Catalyst.Abstractions/P2P/Protocols/IPeerQueryTipRequest.cs @@ -0,0 +1,47 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Reactive.Subjects; +using System.Threading.Tasks; +using Catalyst.Protocol.Peer; +using MultiFormats; + +namespace Catalyst.Abstractions.P2P.Protocols +{ + /// + /// This class is used to validate peers by carrying out a peer challenge response + /// + public interface IPeerQueryTipRequest : IProtocolRequest, IDisposable + { + /// + /// Used to challenge a peer for a response based on the provided public key, ip and port chunks + /// + /// The recipient peer identifier. + /// PeerId holds the chunks we want to validate. + /// bool true means valid and false means not valid + Task QueryPeerTipAsync(MultiAddress recipientPeerIdentifier); + + ReplaySubject QueryTipResponseMessageStreamer { get; } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/PeerChallengerResponse.cs b/src/Catalyst.Abstractions/P2P/Protocols/IPeerQueryTipResponse.cs similarity index 74% rename from src/Catalyst.Core.Lib/P2P/PeerChallengerResponse.cs rename to src/Catalyst.Abstractions/P2P/Protocols/IPeerQueryTipResponse.cs index bb4b57893e..6e9e978efa 100644 --- a/src/Catalyst.Core.Lib/P2P/PeerChallengerResponse.cs +++ b/src/Catalyst.Abstractions/P2P/Protocols/IPeerQueryTipResponse.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,15 +21,15 @@ #endregion -using Catalyst.Abstractions.P2P; using Catalyst.Protocol.Peer; +using Lib.P2P; +using MultiFormats; -namespace Catalyst.Core.Lib.P2P +namespace Catalyst.Abstractions.P2P.Protocols { - public class PeerChallengerResponse : IPeerChallengeResponse + public interface IPeerQueryTipResponse { - public PeerId PeerId { get; } - - public PeerChallengerResponse(PeerId peerId) { PeerId = peerId; } + MultiAddress Address { get; } + Cid DeltaHash { get; } } } diff --git a/src/Catalyst.Abstractions/P2P/Protocols/IProtocol.cs b/src/Catalyst.Abstractions/P2P/Protocols/IProtocol.cs new file mode 100644 index 0000000000..dc7d5a49b9 --- /dev/null +++ b/src/Catalyst.Abstractions/P2P/Protocols/IProtocol.cs @@ -0,0 +1,33 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Protocol.Peer; +using MultiFormats; + +namespace Catalyst.Abstractions.P2P.Protocols +{ + public interface IProtocol + { + MultiAddress Address { get; } + } +} diff --git a/src/Catalyst.Abstractions/P2P/Protocols/IProtocolRequest.cs b/src/Catalyst.Abstractions/P2P/Protocols/IProtocolRequest.cs new file mode 100644 index 0000000000..f71158d4f3 --- /dev/null +++ b/src/Catalyst.Abstractions/P2P/Protocols/IProtocolRequest.cs @@ -0,0 +1,30 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.P2P.Protocols +{ + public interface IProtocolRequest : IProtocol + { + IPeerClient PeerClient { get; } + } +} diff --git a/src/Catalyst.Abstractions/P2P/Protocols/PeerDeltaHistoryRequest.cs b/src/Catalyst.Abstractions/P2P/Protocols/PeerDeltaHistoryRequest.cs new file mode 100644 index 0000000000..0f1d3d1c20 --- /dev/null +++ b/src/Catalyst.Abstractions/P2P/Protocols/PeerDeltaHistoryRequest.cs @@ -0,0 +1,46 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Reactive.Subjects; +using System.Threading.Tasks; +using MultiFormats; + +namespace Catalyst.Abstractions.P2P.Protocols +{ + /// + /// Protocol of requesting index of delta histories from peers + /// + public interface IPeerDeltaHistoryRequest : IProtocolRequest, IDisposable + { + ReplaySubject DeltaHistoryResponseMessageStreamer { get; } + + /// + /// Request an index of delta cid from a peer at a given height. + /// + /// The recipient peer identifier + /// Delta height to request + /// number of deltas requested + Task DeltaHistoryAsync(MultiAddress recipientPeerIdentifier, uint height, uint range); + } +} diff --git a/src/Catalyst.Abstractions/P2P/Protocols/PeerDeltaHistoryResponse.cs b/src/Catalyst.Abstractions/P2P/Protocols/PeerDeltaHistoryResponse.cs new file mode 100644 index 0000000000..0b55fd0e07 --- /dev/null +++ b/src/Catalyst.Abstractions/P2P/Protocols/PeerDeltaHistoryResponse.cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Peer; +using MultiFormats; + +namespace Catalyst.Abstractions.P2P.Protocols +{ + public interface IPeerDeltaHistoryResponse + { + MultiAddress Address { get; } + ICollection DeltaCid { get; } + } +} diff --git a/src/Catalyst.Abstractions/P2P/ReputationSystem/IPeerReputationChange.cs b/src/Catalyst.Abstractions/P2P/ReputationSystem/IPeerReputationChange.cs index 64c2fe5799..6ba847b798 100644 --- a/src/Catalyst.Abstractions/P2P/ReputationSystem/IPeerReputationChange.cs +++ b/src/Catalyst.Abstractions/P2P/ReputationSystem/IPeerReputationChange.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,13 +22,13 @@ #endregion using Catalyst.Abstractions.Config; -using Catalyst.Protocol.Peer; +using Nethermind.Core; namespace Catalyst.Abstractions.P2P.ReputationSystem { public interface IPeerReputationChange { - PeerId PeerId { get; } + Address Address { get; } IReputationEvents ReputationEvent { get; } } } diff --git a/src/Catalyst.Abstractions/Registry/IRegistryBase.cs b/src/Catalyst.Abstractions/Registry/IRegistryBase.cs index 10f5f7e7af..76a3994799 100644 --- a/src/Catalyst.Abstractions/Registry/IRegistryBase.cs +++ b/src/Catalyst.Abstractions/Registry/IRegistryBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Registry/RegistryBase.cs b/src/Catalyst.Abstractions/Registry/RegistryBase.cs index e53dd3c831..d887b24fb7 100644 --- a/src/Catalyst.Abstractions/Registry/RegistryBase.cs +++ b/src/Catalyst.Abstractions/Registry/RegistryBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Repository/ExtensionMethods.cs b/src/Catalyst.Abstractions/Repository/ExtensionMethods.cs new file mode 100644 index 0000000000..58f87e63c3 --- /dev/null +++ b/src/Catalyst.Abstractions/Repository/ExtensionMethods.cs @@ -0,0 +1,53 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Hashing; +using Catalyst.Protocol.Transaction; +using Nethermind.Core.Crypto; + +namespace Catalyst.Abstractions.Repository +{ + public static class ExtensionMethods + { + /// + /// Gets the document id on the basis of content. + /// + public static string GetDocumentId(this PublicEntry entry, IHashProvider hashProvider) => entry.GetHash(hashProvider).AsDocumentId(); + + /// + /// Gets the hash out of the public entry. + /// + /// The entry. + /// The hash provider. + /// The hash value. + public static Keccak GetHash(this PublicEntry entry, IHashProvider hashProvider) + { + return new Keccak(hashProvider.ComputeMultiHash(entry).Digest); + } + + /// + /// Transforms to a unified document id. + /// + public static string AsDocumentId(this Keccak keccak) => keccak.ToString(false); + } +} diff --git a/src/Catalyst.Abstractions/Repository/IDocument.cs b/src/Catalyst.Abstractions/Repository/IDocument.cs index b10062ee67..15978a1af1 100644 --- a/src/Catalyst.Abstractions/Repository/IDocument.cs +++ b/src/Catalyst.Abstractions/Repository/IDocument.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Repository/IPeerRepository.cs b/src/Catalyst.Abstractions/Repository/IPeerRepository.cs new file mode 100644 index 0000000000..e859452a1e --- /dev/null +++ b/src/Catalyst.Abstractions/Repository/IPeerRepository.cs @@ -0,0 +1,62 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using Catalyst.Core.Lib.P2P.Models; +using Catalyst.Protocol.Peer; +using MultiFormats; +using Nethermind.Core; + +namespace Catalyst.Abstractions.P2P.Repository +{ + public interface IPeerRepository : IDisposable + { + Peer Get(string id); + Peer Get(PeerId id); + IEnumerable GetAll(); + IEnumerable GetActivePeers(int count); + IEnumerable GetActivePoaPeers(); + IEnumerable GetRandomPeers(int count); + IEnumerable GetPeersByAddress(MultiAddress address); + IEnumerable GetPeersByKvmAddress(Address kvmAddress); + IEnumerable GetPoaPeersByPublicKey(string publicKeyBase58); + + void Add(Peer peer); + void Add(IEnumerable peer); + + IEnumerable TakeHighestReputationPeers(int page, int count); + + void Update(Peer peer); + + void Delete(Peer peer); + void Delete(string id); + + uint DeletePeersByAddress(MultiAddress address); + + bool Exists(string id); + + int Count(); + int CountActivePeers(); + } +} diff --git a/src/Catalyst.Abstractions/Rpc/Authentication/IAuthCredentials.cs b/src/Catalyst.Abstractions/Rpc/Authentication/IAuthCredentials.cs index 307ff17256..75c4ad349e 100644 --- a/src/Catalyst.Abstractions/Rpc/Authentication/IAuthCredentials.cs +++ b/src/Catalyst.Abstractions/Rpc/Authentication/IAuthCredentials.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,12 +28,8 @@ namespace Catalyst.Abstractions.Rpc.Authentication { public interface IAuthCredentials : IAuditable, IDocument { - /// Gets or sets the public key. - /// The public key. - string PublicKey { get; set; } - - /// Gets or sets the ip address. - /// The ip address. - string IpAddress { get; set; } + /// Gets or sets the address. + /// The multi address. + string Address { get; set; } } } diff --git a/src/Catalyst.Abstractions/Rpc/Authentication/IAuthenticationStrategy.cs b/src/Catalyst.Abstractions/Rpc/Authentication/IAuthenticationStrategy.cs index 58c0325dff..d5bef98060 100644 --- a/src/Catalyst.Abstractions/Rpc/Authentication/IAuthenticationStrategy.cs +++ b/src/Catalyst.Abstractions/Rpc/Authentication/IAuthenticationStrategy.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,6 +22,7 @@ #endregion using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Abstractions.Rpc.Authentication { @@ -33,6 +34,6 @@ public interface IAuthenticationStrategy /// Authenticates the specified peer identifier. /// The peer identifier. /// if [true] then whitelist node operator messages otherwise if [false] block messages - bool Authenticate(PeerId peerIdentifier); + bool Authenticate(MultiAddress Addressentifier); } } diff --git a/src/Catalyst.Abstractions/Rpc/IO/Messaging/Correlation/IRpcMessageCorrelationManager.cs b/src/Catalyst.Abstractions/Rpc/IO/Messaging/Correlation/IRpcMessageCorrelationManager.cs index 24a630e310..217d208bbb 100644 --- a/src/Catalyst.Abstractions/Rpc/IO/Messaging/Correlation/IRpcMessageCorrelationManager.cs +++ b/src/Catalyst.Abstractions/Rpc/IO/Messaging/Correlation/IRpcMessageCorrelationManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Rpc/IRpcClientConfig.cs b/src/Catalyst.Abstractions/Rpc/IRpcClientConfig.cs index 1cf27b9d16..86b0deba87 100644 --- a/src/Catalyst.Abstractions/Rpc/IRpcClientConfig.cs +++ b/src/Catalyst.Abstractions/Rpc/IRpcClientConfig.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,17 +21,15 @@ #endregion -using System.Net; +using MultiFormats; namespace Catalyst.Abstractions.Rpc { public interface IRpcClientConfig { + MultiAddress Address { get; set; } string NodeId { get; set; } - IPAddress HostAddress { get; set; } - int Port { get; set; } string PfxFileName { get; set; } string SslCertPassword { get; set; } - string PublicKey { get; set; } } } diff --git a/src/Catalyst.Abstractions/Rpc/IRpcNodesSettings.cs b/src/Catalyst.Abstractions/Rpc/IRpcNodesSettings.cs index e0e305bf56..54bccab7e6 100644 --- a/src/Catalyst.Abstractions/Rpc/IRpcNodesSettings.cs +++ b/src/Catalyst.Abstractions/Rpc/IRpcNodesSettings.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Rpc/IRpcServerSettings.cs b/src/Catalyst.Abstractions/Rpc/IRpcServerSettings.cs index 8eb5ad0cc3..a54ee84aed 100644 --- a/src/Catalyst.Abstractions/Rpc/IRpcServerSettings.cs +++ b/src/Catalyst.Abstractions/Rpc/IRpcServerSettings.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,16 +21,15 @@ #endregion -using System.Net; using Microsoft.Extensions.Configuration; +using MultiFormats; namespace Catalyst.Abstractions.Rpc { public interface IRpcServerSettings { - int Port { get; } string PfxFileName { get; } - IPAddress BindAddress { get; } + MultiAddress Address { get; } IConfigurationRoot NodeConfig { get; } } } diff --git a/src/Catalyst.Core.Lib/Repository/Attributes/AuditableAttribute.cs b/src/Catalyst.Abstractions/Service/Attributes/AuditableAttribute.cs similarity index 95% rename from src/Catalyst.Core.Lib/Repository/Attributes/AuditableAttribute.cs rename to src/Catalyst.Abstractions/Service/Attributes/AuditableAttribute.cs index 9459dc5f8a..3b320e29f8 100644 --- a/src/Catalyst.Core.Lib/Repository/Attributes/AuditableAttribute.cs +++ b/src/Catalyst.Abstractions/Service/Attributes/AuditableAttribute.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,7 +25,7 @@ using Catalyst.Abstractions.Attributes; using SharpRepository.Repository.Aspects; -namespace Catalyst.Core.Lib.Repository.Attributes +namespace Catalyst.Abstractions.Service.Attributes { public sealed class AuditAttribute : RepositoryActionBaseAttribute { diff --git a/src/Catalyst.Abstractions/Sync/Interfaces/IDeltaHeightRanker.cs b/src/Catalyst.Abstractions/Sync/Interfaces/IDeltaHeightRanker.cs new file mode 100644 index 0000000000..b1e162da78 --- /dev/null +++ b/src/Catalyst.Abstractions/Sync/Interfaces/IDeltaHeightRanker.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Protocol.IPPN; +using Catalyst.Protocol.Peer; +using MultiFormats; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Catalyst.Abstractions.Sync.Interfaces +{ + public interface IDeltaHeightRanker : IDisposable + { + IEnumerable GetPeers(); + + void Add(MultiAddress key, LatestDeltaHashResponse value); + + int Count(); + + IOrderedEnumerable> GetMessagesByMostPopular(); + } +} diff --git a/src/Catalyst.Abstractions/Sync/Interfaces/IDeltaHeightWatcher.cs b/src/Catalyst.Abstractions/Sync/Interfaces/IDeltaHeightWatcher.cs new file mode 100644 index 0000000000..2c9a69af73 --- /dev/null +++ b/src/Catalyst.Abstractions/Sync/Interfaces/IDeltaHeightWatcher.cs @@ -0,0 +1,41 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Protocol.Deltas; + +namespace Catalyst.Abstractions.Sync.Interfaces +{ + public interface IDeltaHeightWatcher : IDisposable + { + IDeltaHeightRanker DeltaHeightRanker { get; } + DeltaIndex LatestDeltaHash { get; } + Task WaitForDeltaIndexAsync(TimeSpan timeout); + Task WaitForDeltaIndexAsync(TimeSpan timeout, CancellationToken cancellationToken); + Task GetHighestDeltaIndexAsync(); + void Start(); + void Stop(); + } +} diff --git a/src/Catalyst.Abstractions/Sync/Interfaces/IPeerSyncManager.cs b/src/Catalyst.Abstractions/Sync/Interfaces/IPeerSyncManager.cs new file mode 100644 index 0000000000..9a1a246a41 --- /dev/null +++ b/src/Catalyst.Abstractions/Sync/Interfaces/IPeerSyncManager.cs @@ -0,0 +1,40 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Protocol.Deltas; + +namespace Catalyst.Abstractions.Sync.Interfaces +{ + public interface IPeerSyncManager : IDisposable + { + IObservable> ScoredDeltaIndexRange { get; } + void GetDeltaIndexRangeFromPeers(ulong index, int range); + Task WaitForPeersAsync(CancellationToken cancellationToken = default); + void Start(); + void Stop(); + } +} diff --git a/src/Catalyst.Abstractions/Sync/Interfaces/IRankedItem.cs b/src/Catalyst.Abstractions/Sync/Interfaces/IRankedItem.cs new file mode 100644 index 0000000000..85c33accd4 --- /dev/null +++ b/src/Catalyst.Abstractions/Sync/Interfaces/IRankedItem.cs @@ -0,0 +1,31 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.Sync.Interfaces +{ + public interface IRankedItem + { + T Item { set; get; } + int Score { set; get; } + } +} diff --git a/src/Catalyst.Core.Modules.Ledger/ILedgerSynchroniser.cs b/src/Catalyst.Abstractions/Sync/Interfaces/ISynchronizer.cs similarity index 53% rename from src/Catalyst.Core.Modules.Ledger/ILedgerSynchroniser.cs rename to src/Catalyst.Abstractions/Sync/Interfaces/ISynchronizer.cs index cdc3bfab2a..3b0551076b 100644 --- a/src/Catalyst.Core.Modules.Ledger/ILedgerSynchroniser.cs +++ b/src/Catalyst.Abstractions/Sync/Interfaces/ISynchronizer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,32 +21,30 @@ #endregion +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Core.Abstractions.Sync; +using Lib.P2P; +using System; using System.Collections.Generic; using System.Threading; -using Catalyst.Abstractions.Consensus.Deltas; -using LibP2P; +using System.Threading.Tasks; -namespace Catalyst.Core.Modules.Ledger +namespace Catalyst.Abstractions.Sync.Interfaces { - public interface ILedgerSynchroniser + public interface ISynchroniser : IDisposable { - /// - /// Starts a process that retrieves the deltas between the - /// and the , and adds them to the cache. - /// - /// - /// Hash of the latest known Delta seen on the protocol, - /// from the point of view of this node. - /// - /// The hash of the delta up to which we want to synchronise the ledger. - /// Provides a way to cancel the synchronisation task before it ends. + SyncState State { set; get; } + IDeltaCache DeltaCache { get; } + + void UpdateState(ulong _latestKnownDeltaNumber); + + IObservable SyncCompleted { get; } + + Task StartAsync(CancellationToken cancellationToken = default); + Task StopAsync(CancellationToken cancellationToken = default); + IEnumerable CacheDeltasBetween(Cid latestKnownDeltaHash, Cid targetDeltaHash, CancellationToken cancellationToken); - - /// - /// A cache used to store the full Delta object when a synchronisation is triggered. - /// - IDeltaCache DeltaCache { get; } } } diff --git a/src/Catalyst.Abstractions/Sync/SyncState.cs b/src/Catalyst.Abstractions/Sync/SyncState.cs new file mode 100644 index 0000000000..1422470c2d --- /dev/null +++ b/src/Catalyst.Abstractions/Sync/SyncState.cs @@ -0,0 +1,34 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Abstractions.Sync +{ + public class SyncState + { + public bool IsSynchronized { set; get; } + public ulong StartingBlock { set; get; } + public ulong CurrentBlock { set; get; } + public ulong HighestBlock { set; get; } + public bool IsRunning { set; get; } + } +} diff --git a/src/Catalyst.Abstractions/Types/AccountTypes.cs b/src/Catalyst.Abstractions/Types/AccountTypes.cs index 9b2bc2e6cc..12564408c2 100644 --- a/src/Catalyst.Abstractions/Types/AccountTypes.cs +++ b/src/Catalyst.Abstractions/Types/AccountTypes.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Types/Byte32.cs b/src/Catalyst.Abstractions/Types/Byte32.cs index e366695164..ba5fdf09a2 100644 --- a/src/Catalyst.Abstractions/Types/Byte32.cs +++ b/src/Catalyst.Abstractions/Types/Byte32.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Types/Byte64.cs b/src/Catalyst.Abstractions/Types/Byte64.cs index 286935e358..34302ab934 100644 --- a/src/Catalyst.Abstractions/Types/Byte64.cs +++ b/src/Catalyst.Abstractions/Types/Byte64.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Types/FileTransferResponseCodeTypes.cs b/src/Catalyst.Abstractions/Types/FileTransferResponseCodeTypes.cs index 1e9fe9755b..3fc142c1f0 100644 --- a/src/Catalyst.Abstractions/Types/FileTransferResponseCodeTypes.cs +++ b/src/Catalyst.Abstractions/Types/FileTransferResponseCodeTypes.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Types/FixedSizedBytes.cs b/src/Catalyst.Abstractions/Types/FixedSizedBytes.cs index e781692934..635e373f5a 100644 --- a/src/Catalyst.Abstractions/Types/FixedSizedBytes.cs +++ b/src/Catalyst.Abstractions/Types/FixedSizedBytes.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -49,8 +49,8 @@ protected byte[] RawBytes public static T RandomBytes() { - var newBytes = new T(); - var random = new Random(); + T newBytes = new(); + Random random = new(); random.NextBytes(newBytes.RawBytes); return newBytes; } diff --git a/src/Catalyst.Abstractions/Types/KeyRegistryTypes.cs b/src/Catalyst.Abstractions/Types/KeyRegistryTypes.cs index b282e23b01..4f55a84b79 100644 --- a/src/Catalyst.Abstractions/Types/KeyRegistryTypes.cs +++ b/src/Catalyst.Abstractions/Types/KeyRegistryTypes.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,7 +32,7 @@ private KeyRegistryTypes(int id, string name) : base(id, name) { } private sealed class DefaultSigningKey : KeyRegistryTypes { - public DefaultSigningKey() : base(1, "defaultSigningKey") { } + public DefaultSigningKey() : base(1, "self") { } } } } diff --git a/src/Catalyst.Abstractions/Types/MessageTypes.cs b/src/Catalyst.Abstractions/Types/MessageTypes.cs index f1de709d57..01e354286c 100644 --- a/src/Catalyst.Abstractions/Types/MessageTypes.cs +++ b/src/Catalyst.Abstractions/Types/MessageTypes.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Types/ModuleTypes.cs b/src/Catalyst.Abstractions/Types/ModuleTypes.cs index d420acb9f4..d85e4a83b2 100644 --- a/src/Catalyst.Abstractions/Types/ModuleTypes.cs +++ b/src/Catalyst.Abstractions/Types/ModuleTypes.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Types/NeighbourStateTypes.cs b/src/Catalyst.Abstractions/Types/NeighbourStateTypes.cs index 25dd8a6cef..5cb0e0b8ca 100644 --- a/src/Catalyst.Abstractions/Types/NeighbourStateTypes.cs +++ b/src/Catalyst.Abstractions/Types/NeighbourStateTypes.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Types/NetworkTypes.cs b/src/Catalyst.Abstractions/Types/NetworkTypes.cs index 054d2a808a..d2884846d0 100644 --- a/src/Catalyst.Abstractions/Types/NetworkTypes.cs +++ b/src/Catalyst.Abstractions/Types/NetworkTypes.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Types/PasswordRegistryTypes.cs b/src/Catalyst.Abstractions/Types/PasswordRegistryTypes.cs index 4a46b03f52..01b78b3121 100644 --- a/src/Catalyst.Abstractions/Types/PasswordRegistryTypes.cs +++ b/src/Catalyst.Abstractions/Types/PasswordRegistryTypes.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,6 @@ namespace Catalyst.Abstractions.Types public class PasswordRegistryTypes : Enumeration { public static readonly PasswordRegistryTypes CertificatePassword = new CertificatePasswordTypes(); - public static readonly PasswordRegistryTypes IpfsPassword = new IpfsPasswordTypes(); public static readonly PasswordRegistryTypes DefaultNodePassword = new DefaultNodePasswordTypes(); private PasswordRegistryTypes(int id, string name) : base(id, name) { } @@ -38,14 +37,9 @@ private sealed class CertificatePasswordTypes : PasswordRegistryTypes public CertificatePasswordTypes() : base(1, "certificatePasswordKey") { } } - private sealed class IpfsPasswordTypes : PasswordRegistryTypes - { - public IpfsPasswordTypes() : base(2, "ipfsPasswordKey") { } - } - private sealed class DefaultNodePasswordTypes : PasswordRegistryTypes { - public DefaultNodePasswordTypes() : base(4, "defaultNodePasswordKey") { } + public DefaultNodePasswordTypes() : base(2, "defaultNodePasswordKey") { } } } } diff --git a/src/Catalyst.Abstractions/Types/PeerMessageTypes.cs b/src/Catalyst.Abstractions/Types/PeerMessageTypes.cs index dfd12bf46d..eaf04bc321 100644 --- a/src/Catalyst.Abstractions/Types/PeerMessageTypes.cs +++ b/src/Catalyst.Abstractions/Types/PeerMessageTypes.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Types/ReputationEventType.cs b/src/Catalyst.Abstractions/Types/ReputationEventType.cs index 0bb05cae5d..ef3b4c7094 100644 --- a/src/Catalyst.Abstractions/Types/ReputationEventType.cs +++ b/src/Catalyst.Abstractions/Types/ReputationEventType.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,7 +32,8 @@ public class ReputationEventType : Enumeration, IReputationEvents public static readonly ReputationEventType ResponseReceived = new ResponseReceivedEventType(); public static readonly ReputationEventType UnCorrelatableMessage = new UnCorrelatableMessageEventType(); public static readonly ReputationEventType InvalidMessageSignature = new InvalidMessageSignatureEventType(); - + public static readonly ReputationEventType VoterIsNotProducer = new VoterIsNotProducerEventType(); + public int Amount { get; set; } private ReputationEventType(int id, string name) : base(id, name) { } @@ -72,5 +73,13 @@ private sealed class InvalidMessageSignatureEventType : ReputationEventType { public InvalidMessageSignatureEventType() : base(4, "invalidMessageSignature") { Amount = -1000; } } + + /// + /// Fired when a peer votes when it is not authorized to vote because it is not in the voter pool. + /// + private sealed class VoterIsNotProducerEventType : ReputationEventType + { + public VoterIsNotProducerEventType() : base(5, "voterIsNotProducer") { Amount = -1000; } + } } } diff --git a/src/Catalyst.Abstractions/Types/RpcMessages.cs b/src/Catalyst.Abstractions/Types/RpcMessages.cs index de0b7c425e..a4ee208efd 100644 --- a/src/Catalyst.Abstractions/Types/RpcMessages.cs +++ b/src/Catalyst.Abstractions/Types/RpcMessages.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -46,7 +46,7 @@ public class RpcMessages static RpcMessages() { // ReSharper disable once CollectionNeverQueried.Local - var messageMap = new Dictionary(); + Dictionary messageMap = new(); var types = AppDomain.CurrentDomain.GetAssemblies() .SelectMany(t => t.GetTypes()) .Where(t => t.IsClass && t.Namespace == MessageNamespace diff --git a/src/Catalyst.Core.Lib/Util/DateTimeUtil.cs b/src/Catalyst.Abstractions/Util/DateTimeUtil.cs similarity index 96% rename from src/Catalyst.Core.Lib/Util/DateTimeUtil.cs rename to src/Catalyst.Abstractions/Util/DateTimeUtil.cs index 342326fcfd..f976cf0b9b 100644 --- a/src/Catalyst.Core.Lib/Util/DateTimeUtil.cs +++ b/src/Catalyst.Abstractions/Util/DateTimeUtil.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,7 +24,7 @@ using System; using Dawn; -namespace Catalyst.Core.Lib.Util +namespace Catalyst.Abstractions.Lib.Util { public static class DateTimeUtil { diff --git a/src/Catalyst.Abstractions/Util/ICancellationTokenProvider.cs b/src/Catalyst.Abstractions/Util/ICancellationTokenProvider.cs index f81ef608a6..5199d4bc8f 100644 --- a/src/Catalyst.Abstractions/Util/ICancellationTokenProvider.cs +++ b/src/Catalyst.Abstractions/Util/ICancellationTokenProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,7 +30,7 @@ namespace Catalyst.Abstractions.Util /// public interface ICancellationTokenProvider { - CancellationTokenSource CancellationTokenSource { get; set; } + CancellationTokenSource CancellationTokenSource { get; } bool HasTokenCancelled(); } diff --git a/src/Catalyst.Abstractions/Util/IChangeTokenProvider.cs b/src/Catalyst.Abstractions/Util/IChangeTokenProvider.cs index ca0af3a207..95057bb097 100644 --- a/src/Catalyst.Abstractions/Util/IChangeTokenProvider.cs +++ b/src/Catalyst.Abstractions/Util/IChangeTokenProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Abstractions/Validators/ITransactionValidator.cs b/src/Catalyst.Abstractions/Validators/ITransactionValidator.cs index 0f129f90a5..67cdfc56d8 100644 --- a/src/Catalyst.Abstractions/Validators/ITransactionValidator.cs +++ b/src/Catalyst.Abstractions/Validators/ITransactionValidator.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,12 +21,12 @@ #endregion -using Catalyst.Protocol.Wire; +using Catalyst.Protocol.Transaction; namespace Catalyst.Abstractions.Validators { public interface ITransactionValidator { - bool ValidateTransaction(TransactionBroadcast transactionBroadcast); + bool ValidateTransaction(PublicEntry transaction); } } diff --git a/src/Catalyst.Abstractions/Validators/IValidatorReader.cs b/src/Catalyst.Abstractions/Validators/IValidatorReader.cs new file mode 100644 index 0000000000..0fd0a70eca --- /dev/null +++ b/src/Catalyst.Abstractions/Validators/IValidatorReader.cs @@ -0,0 +1,33 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; + +namespace Catalyst.Abstractions.Validators +{ + public interface IValidatorReader + { + void AddValidatorSet(IList validatorSets, long startBlock, IConfigurationSection configurationSection); + } +} diff --git a/src/Catalyst.Abstractions/Validators/IValidatorSet.cs b/src/Catalyst.Abstractions/Validators/IValidatorSet.cs new file mode 100644 index 0000000000..914e975e7d --- /dev/null +++ b/src/Catalyst.Abstractions/Validators/IValidatorSet.cs @@ -0,0 +1,34 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Nethermind.Core; +using System.Collections.Generic; + +namespace Catalyst.Abstractions.Validators +{ + public interface IValidatorSet + { + long StartBlock { get; } + IEnumerable
GetValidators(); + } +} diff --git a/src/Catalyst.Abstractions/Validators/IValidatorSetStore.cs b/src/Catalyst.Abstractions/Validators/IValidatorSetStore.cs new file mode 100644 index 0000000000..6ef131f2b0 --- /dev/null +++ b/src/Catalyst.Abstractions/Validators/IValidatorSetStore.cs @@ -0,0 +1,31 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Abstractions.Validators +{ + public interface IValidatorSetStore + { + void Add(IValidatorSet validatorSet); + IValidatorSet Get(long startBlock); + } +} diff --git a/src/Catalyst.Benchmark/Catalyst.Abstractions/CryptoBenchmark.cs b/src/Catalyst.Benchmark/Catalyst.Abstractions/CryptoBenchmark.cs new file mode 100644 index 0000000000..86658c7a76 --- /dev/null +++ b/src/Catalyst.Benchmark/Catalyst.Abstractions/CryptoBenchmark.cs @@ -0,0 +1,123 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Protocol.Cryptography; +using Catalyst.Protocol.Network; +using Catalyst.Protocol.Transaction; +using Catalyst.Protocol.Wire; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; + +namespace Catalyst.Benchmark.Catalyst.Abstractions +{ + [MemoryDiagnoser] + [SimpleJob(RuntimeMoniker.CoreRt30)] + public class CryptoBenchmark + { + public CryptoBenchmark() + { + _context = new SigningContext + { + NetworkType = NetworkType.Mainnet, + SignatureType = SignatureType.TransactionConfidential + }; + + var key = ByteString.CopyFrom(new byte[32]); + var amount = ByteString.CopyFrom(new byte[32]); + + _transaction = new TransactionBroadcast + { + PublicEntry = new PublicEntry + { + Amount = amount, + Nonce = 1, + SenderAddress = key, + ReceiverAddress = key, + GasPrice = amount + } + }; + + _crypto = new NoopCryptoContext(); + } + + private readonly SigningContext _context; + private readonly TransactionBroadcast _transaction; + private readonly ICryptoContext _crypto; + + [Benchmark] + public bool SignVerify_with_ToByteArray() + { + var signature = _crypto.Sign(null, _transaction.ToByteArray(), _context.ToByteArray()); + return _crypto.Verify(signature, _transaction.ToByteArray(), _context.ToByteArray()); + } + + [Benchmark] + public bool SignVerify_with_embedded_serialization() + { + var signature = _crypto.Sign(null, _transaction, _context); + return _crypto.Verify(signature, _transaction, _context); + } + + internal class NoopCryptoContext : ICryptoContext + { + public ISignature Sign(IPrivateKey privateKey, ReadOnlySpan message, ReadOnlySpan context) + { + return null; + } + + public bool Verify(ISignature signature, ReadOnlySpan message, ReadOnlySpan context) + { + return true; + } + + public bool BatchVerify(IList signatures, IList messages, ReadOnlySpan context) { throw new NotImplementedException(); } + + public int PrivateKeyLength => throw new NotImplementedException(); + public int PublicKeyLength => throw new NotImplementedException(); + public int SignatureLength => throw new NotImplementedException(); + public int SignatureContextMaxLength => throw new NotImplementedException(); + public IPrivateKey GeneratePrivateKey() { throw new NotImplementedException(); } + + public IPublicKey GetPublicKeyFromPrivateKey(IPrivateKey privateKey) + { + throw new NotImplementedException(); + } + + public IPublicKey GetPublicKeyFromBytes(byte[] publicKeyBytes) { throw new NotImplementedException(); } + public IPrivateKey GetPrivateKeyFromBytes(byte[] privateKeyBytes) { throw new NotImplementedException(); } + + public ISignature GetSignatureFromBytes(byte[] signatureBytes, byte[] publicKeyBytes) + { + throw new NotImplementedException(); + } + + public byte[] ExportPrivateKey(IPrivateKey privateKey) { throw new NotImplementedException(); } + public byte[] ExportPublicKey(IPublicKey publicKey) { throw new NotImplementedException(); } + } + } +} diff --git a/src/Catalyst.Benchmark/Catalyst.Abstractions/HashingBenchmark.cs b/src/Catalyst.Benchmark/Catalyst.Abstractions/HashingBenchmark.cs new file mode 100644 index 0000000000..d4bcc2d98e --- /dev/null +++ b/src/Catalyst.Benchmark/Catalyst.Abstractions/HashingBenchmark.cs @@ -0,0 +1,96 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using System.Security.Cryptography; +using BenchmarkDotNet.Attributes; +using BenchmarkDotNet.Jobs; +using Catalyst.Abstractions.Hashing; +using Catalyst.Core.Modules.Hashing; +using Catalyst.Protocol.Transaction; +using Google.Protobuf; +using MultiFormats; +using MultiFormats.Registry; +using Nethermind.Core.Extensions; + +namespace Catalyst.Benchmark.Catalyst.Abstractions +{ + [MemoryDiagnoser] + [SimpleJob(RuntimeMoniker.CoreRt30)] + public class HashingBenchmark + { + public HashingBenchmark() + { + const string name = nameof(NoopHash); + + HashingAlgorithm.Register(name, 1234123421, NoopHash.DigestSize, () => new NoopHash()); + + var bytes = ByteString.CopyFrom(Enumerable.Range(1, 32).Select(i => (byte) i).ToArray()); + var amount = ByteString.CopyFrom(343434.ToByteArray()); + + _entry = new PublicEntry + { + Amount = amount, + GasPrice = amount, + ReceiverAddress = bytes, + SenderAddress = bytes + }; + + _provider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata(name)); + } + + private readonly HashProvider _provider; + private readonly PublicEntry _entry; + private readonly byte[] _salt = {1, 2, 3, 4}; + + [Benchmark] + public MultiHash Concat_manual() { return _provider.ComputeMultiHash(_entry.ToByteArray().Concat(_salt)); } + + [Benchmark] + public MultiHash Concat_embedded() { return _provider.ComputeMultiHash(_entry, _salt); } + + internal class NoopHash : HashAlgorithm + { + public const int DigestSize = 32; + private static readonly byte[] Value = new byte[DigestSize]; + + protected override void HashCore(byte[] array, int ibStart, int cbSize) { } + + protected override byte[] HashFinal() { return Value; } + + public override void Initialize() { } + + protected override bool TryHashFinal(Span destination, out int bytesWritten) + { + bytesWritten = destination.Length; + return true; + } + + protected override void HashCore(ReadOnlySpan source) + { + // this is a noop hasher that is used only for benchmarking purposes, to shaw the overhead of non-hashing operations + } + } + } +} diff --git a/src/Catalyst.Benchmark/Catalyst.Benchmark.csproj b/src/Catalyst.Benchmark/Catalyst.Benchmark.csproj new file mode 100644 index 0000000000..19d177af39 --- /dev/null +++ b/src/Catalyst.Benchmark/Catalyst.Benchmark.csproj @@ -0,0 +1,30 @@ + + + + Exe + net6.0 + latest + true + false + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Catalyst.Benchmark/Ed25519BouncyCastle.cs b/src/Catalyst.Benchmark/Ed25519BouncyCastle.cs new file mode 100644 index 0000000000..939ba6e49a --- /dev/null +++ b/src/Catalyst.Benchmark/Ed25519BouncyCastle.cs @@ -0,0 +1,92 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using BenchmarkDotNet.Attributes; +using Org.BouncyCastle.Math.EC.Rfc8032; +using Org.BouncyCastle.Security; + +namespace Catalyst.Benchmark +{ + [CategoriesColumn] + [BenchmarkCategory("ed25519")] + public class Ed25519BouncyCastle + { + private static readonly SecureRandom Random = new(); + private byte[] _privateKey; + private byte[] _publicKey; + private byte[] _message; + private byte[] _signature; + private const int MessageLength = 32; + + public Ed25519BouncyCastle() + { + Ed25519.Precompute(); + } + + [GlobalSetup(Target = nameof(GeneratePublicKey))] + public void SetupGenerateKey(){ + _privateKey = new byte[Ed25519.SecretKeySize]; + _publicKey = new byte[Ed25519.PublicKeySize]; + Random.NextBytes(_privateKey); + } + + [GlobalSetup(Target = nameof(Sign))] + public void SetupSign(){ + _message = new byte[MessageLength]; + _signature = new byte[Ed25519.SignatureSize]; + + SetupGenerateKey(); + + Ed25519.GeneratePublicKey(_privateKey, 0, _publicKey, 0); + Random.NextBytes(_message); + } + + [GlobalSetup(Target = nameof(Verify))] + public void SetupVerify(){ + SetupSign(); + Ed25519.Sign(_privateKey, 0, _message, 0, MessageLength, _signature, 0); + + } + + [Benchmark] + [BenchmarkCategory("keygen")] + public void GeneratePublicKey() + { + Ed25519.GeneratePublicKey(_privateKey, 0, _publicKey, 0); + } + + [Benchmark] + [BenchmarkCategory("sign")] + public void Sign() + { + Ed25519.Sign(_privateKey, 0, _message, 0, MessageLength, _signature, 0); + } + + [Benchmark] + [BenchmarkCategory("verify")] + public bool Verify() + { + return Ed25519.Verify(_signature, 0, _publicKey, 0, _message, 0, MessageLength); + } + } +} diff --git a/src/Catalyst.Benchmark/Ed25519NSec.cs b/src/Catalyst.Benchmark/Ed25519NSec.cs new file mode 100644 index 0000000000..0820c1615c --- /dev/null +++ b/src/Catalyst.Benchmark/Ed25519NSec.cs @@ -0,0 +1,87 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using BenchmarkDotNet.Attributes; +using NSec.Cryptography; +using Org.BouncyCastle.Security; + +namespace Catalyst.Benchmark +{ + [CategoriesColumn] + [BenchmarkCategory("ed25519")] + public class Ed25519NSec + { + private static readonly SecureRandom Random = new(); + + private readonly SignatureAlgorithm _algorithm = SignatureAlgorithm.Ed25519; + private byte[] _message; + private byte[] _signature; + private Key _key; + + [GlobalSetup(Target = nameof(GeneratePublicKey))] + public void SetupGenerateKey(){ + _key = new Key(_algorithm); + } + + [GlobalSetup(Target = nameof(Sign))] + public void SetupSign(){ + _message = new byte[32]; + _signature = new byte[64]; + SetupGenerateKey(); + _key = Key.Create(_algorithm); + Random.NextBytes(_message); + } + + [GlobalSetup(Target = nameof(Verify))] + public void SetupVerify(){ + SetupSign(); + ReadOnlySpan m = _message; + _signature = _algorithm.Sign(_key, m); + } + + [Benchmark] + [BenchmarkCategory("keygen")] + public void GeneratePublicKey(){ + _key=Key.Create(_algorithm); + } + + [Benchmark] + [BenchmarkCategory("sign")] + public void Sign() + { + ReadOnlySpan m = _message; + _algorithm.Sign(_key, m); + } + + [Benchmark] + [BenchmarkCategory("verify")] + public bool Verify() + { + ReadOnlySpan m = _message; + ReadOnlySpan sig = _signature; + return _algorithm.Verify(_key.PublicKey,m,sig); + } + + } +} diff --git a/src/Catalyst.Benchmark/Ed25519phCatalystFfi.cs b/src/Catalyst.Benchmark/Ed25519phCatalystFfi.cs new file mode 100644 index 0000000000..da36aed875 --- /dev/null +++ b/src/Catalyst.Benchmark/Ed25519phCatalystFfi.cs @@ -0,0 +1,128 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using BenchmarkDotNet.Attributes; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Core.Modules.Cryptography.BulletProofs; +using System.Collections.Generic; +using System.Text; + +namespace Catalyst.Benchmark +{ + [CategoriesColumn] + [BenchmarkCategory("ed25519", "ed25519ph")] + public class Ed25519phCatalystFfi + { + private readonly ICryptoContext _cryptoContext = new FfiWrapper(); + private static readonly Random Random = new(); + private IPrivateKey _privateKey; + private byte[] _message; + private byte[] _context; + private ISignature _signature; + private IList _signatures; + private List _messages; + + [Params(1, 10, 100, 1000, 10000, 100000)] + public int N { get; set; } + + + [GlobalSetup(Target = nameof(GetPublicKey))] + public void SetupGetPublicKey() + { + _privateKey = _cryptoContext.GeneratePrivateKey(); + } + + [GlobalSetup(Target = nameof(Sign))] + public void SetupSign() + { + _message = new byte[32]; + _context = new byte[32]; + SetupGetPublicKey(); + } + + [GlobalSetup(Target = nameof(Verify))] + public void SetupVerify() + { + SetupSign(); + _signature = _cryptoContext.Sign(_privateKey, _message, _context); + } + + [GlobalSetup(Target = nameof(BatchVerify))] + public void SetupBatchVerify() + { + _messages = new List(); + _signatures = new List(); + _context = Encoding.UTF8.GetBytes("context"); + for (var i = 0; i < N; i++) + { + var bytes = new byte[255]; + Random.NextBytes(bytes); + _messages.Add(bytes); + } + _messages.ForEach(x => + { + _signatures.Add(_cryptoContext.Sign(_cryptoContext.GeneratePrivateKey(), x, _context)); + }); + } + + [Benchmark] + [BenchmarkCategory("keygen")] + public void GeneratePrivateKey() + { + _privateKey = _cryptoContext.GeneratePrivateKey(); + + } + + [Benchmark] + [BenchmarkCategory("getpublickey")] + public void GetPublicKey() + { + _cryptoContext.GetPublicKeyFromPrivateKey(_privateKey); + } + + + + [Benchmark] + [BenchmarkCategory("sign")] + public void Sign() + { + _signature = _cryptoContext.Sign(_privateKey, _message, _context); + } + + [Benchmark] + [BenchmarkCategory("verify")] + public bool Verify() + { + return _cryptoContext.Verify(_signature, _message, _context); + } + + [Benchmark] + [BenchmarkCategory("batchverify", "verify")] + public bool BatchVerify() + { + return _cryptoContext.BatchVerify(_signatures, _messages, _context); + } + + } +} diff --git a/src/Catalyst.Benchmark/Program.cs b/src/Catalyst.Benchmark/Program.cs new file mode 100644 index 0000000000..4a0dd9b38f --- /dev/null +++ b/src/Catalyst.Benchmark/Program.cs @@ -0,0 +1,50 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using BenchmarkDotNet.Configs; +using BenchmarkDotNet.Running; +using BenchmarkDotNet.Validators; + +namespace Catalyst.Benchmark +{ + public class Program + { + public static void Main(string[] args) + { + BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args, new AllowNonOptimized()); + } + + private class AllowNonOptimized : ManualConfig + { + public AllowNonOptimized() + { + AddValidator(JitOptimizationsValidator.DontFailOnError); + AddLogger(DefaultConfig.Instance.GetLoggers().ToArray()); + AddExporter(DefaultConfig.Instance.GetExporters().ToArray()); + AddColumnProvider(DefaultConfig.Instance.GetColumnProviders().ToArray()); + } + } + } +} + diff --git a/src/Catalyst.Benchmark/README.md b/src/Catalyst.Benchmark/README.md new file mode 100644 index 0000000000..43f98febcf --- /dev/null +++ b/src/Catalyst.Benchmark/README.md @@ -0,0 +1,51 @@ +
+ ReDoc logo + + ### Crypto Benchmarking + +[![Discord](https://img.shields.io/discord/629667101774446593?color=blueviolet&label=discord)](https://discord.gg/anTP7xm) +![GitHub followers](https://img.shields.io/github/followers/catalyst-network?style=social) +[![GitHub stars](https://img.shields.io/github/stars/catalyst-network/community?style=social)](https://github.com/catalyst-network/protocol-protobuffs/stargazers) +[![Twitter Follow](https://img.shields.io/twitter/follow/catalystnetorg?style=social)](https://twitter.com/catalystnetorg) +[![Subreddit subscribers](https://img.shields.io/reddit/subreddit-subscribers/catalystnet?style=social)](https://reddit.com/r/catalystnet) +
+ +Benchmarker for various crypto libraries + +## Setup +build: +```shell +dotnet publish -c Release -o out +``` + +## Run benchmarks + +To run all benchmarks and collate them into a single table: +```shell +dotnet out/Catalyst.Benchmark.dll -f '*' --join +``` + +To run single point of comparison eg benchmark the verification method of all libraries: +```shell +dotnet out/Catalyst.Benchmark.dll --anyCategories=verify —-join +``` + +To compare just ed25519 methods (or secp256k1) +```shell +dotnet out/Catalyst.Benchmark.dll --anyCategories=ed25519 --join +``` +To get info about memory allocation add ```-m``` to the console arguments + +To run tests for a single library +```shell +dotnet out/Catalyst.Benchmark.dll +``` +to get console options + +You can also use ```Catalyst.Benchmark.exe``` tool to run benchmarks. + +## Reports + +Reports can be found in the BenchmarkDotNet.Artifacts/results folder. + +[Report 4/1/19](BenchmarkDotNet.Artifacts/results/BenchmarkRun-joined-2019-01-04-01-35-42-report-github.md) diff --git a/src/Catalyst.Benchmark/Secp256k1Wrapped.cs b/src/Catalyst.Benchmark/Secp256k1Wrapped.cs new file mode 100644 index 0000000000..9a14ec96a8 --- /dev/null +++ b/src/Catalyst.Benchmark/Secp256k1Wrapped.cs @@ -0,0 +1,127 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using BenchmarkDotNet.Attributes; +using Org.BouncyCastle.Security; +using Secp256k1Net; + +namespace Catalyst.Benchmark +{ + [CategoriesColumn] + [BenchmarkCategory("secp256k1")] + public class Secp256k1Wrapped + { + private static readonly SecureRandom Random = new(); + + private byte[] _privateKey; + private byte[] _publicKey; + private byte[] _message ; + private byte[] _signature; + readonly Secp256k1 secp256k1; + + public Secp256k1Wrapped() + { + secp256k1 = new Secp256k1(); + } + + private Span GeneratePrivateKey() + { + Span sk = new byte[32]; + do + { + Random.NextBytes(sk); + } + while (!secp256k1.SecretKeyVerify(sk)); + return sk; + } + + [GlobalSetup(Target = nameof(GeneratePublicKey))] + public void SetupGenerateKey(){ + var sk = GeneratePrivateKey(); + _privateKey = sk.ToArray(); + } + + [GlobalSetup(Target = nameof(Sign))] + public void SetupSign(){ + _signature = new byte[64]; + + SetupGenerateKey(); + + Span sk = _privateKey; + + Span pk = new byte[64]; + secp256k1.PublicKeyCreate(pk,sk); + _publicKey=pk.ToArray(); + + Span m = new byte[32]; + Random.NextBytes(m); + _message = m.ToArray(); + } + + [GlobalSetup(Target = nameof(Verify))] + public void SetupVerify(){ + SetupSign(); + + Span sk = _privateKey; + + Span sig = new byte[64]; + secp256k1.Sign(sig, _message, sk); + _signature = sig.ToArray(); + } + + [Benchmark] + [BenchmarkCategory("keygen")] + public void GeneratePublicKey(){ + Span sk = _privateKey; + + Span pk = new byte[64]; + secp256k1.PublicKeyCreate(pk,sk); + } + + [Benchmark] + [BenchmarkCategory("sign")] + public void Sign() + { + Span m = _message; + + Span sk = _privateKey; + + Span sig = new byte[64]; + secp256k1.Sign(sig, m, sk); + } + + [Benchmark] + [BenchmarkCategory("verify")] + public bool Verify() + { + Span sig = _signature; + + Span m = _message; + + Span pk = _publicKey; + + return secp256k1.Verify(sig,m,pk); + } + } +} diff --git a/src/Catalyst.Cli.Tests/Catalyst.Cli.Tests.csproj b/src/Catalyst.Cli.Tests/Catalyst.Cli.Tests.csproj index dc32deee42..b5e9f56b1b 100644 --- a/src/Catalyst.Cli.Tests/Catalyst.Cli.Tests.csproj +++ b/src/Catalyst.Cli.Tests/Catalyst.Cli.Tests.csproj @@ -1,24 +1,35 @@ - + True false - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Cli.Tests.snk true - netcoreapp3.0 + net6.0 Catalyst.Cli.Tests + + 1701;1702;VSTHRD200;CS8002 + - + + + + + - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/AddFileCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/AddFileCommandTests.cs index de5a06a7cb..e08f4e09d8 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/AddFileCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/AddFileCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,33 +22,35 @@ #endregion using System; -using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Autofac; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; using Catalyst.Protocol.Rpc.Node; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class AddFileCommandTests : CliCommandTestsBase { - public static IEnumerable AddFileData => - new List - { - new object[] {"/fake_file_path", false}, - new object[] {AppDomain.CurrentDomain.BaseDirectory + "/Config/addfile_test.json", true} - }; + static object[] AddFileData = + { + new object[] {"/fake_file_path", false}, + new object[] {AppDomain.CurrentDomain.BaseDirectory + "/Config/addfile_test.json", true}, + }; - public AddFileCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Theory] - [MemberData(nameof(AddFileData))] + [TestCaseSource("AddFileData")] public async Task Cli_Can_Send_Add_File_Request(string fileName, bool expectedResult) { var uploadFileTransferFactory = Scope.Resolve(); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/ChangeDataFolderCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/ChangeDataFolderCommandTests.cs index cf5d6c9376..0c31badb9d 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/ChangeDataFolderCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/ChangeDataFolderCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,17 +25,23 @@ using System.IO; using Catalyst.Core.Lib.Config; using Catalyst.Protocol.Rpc.Node; +using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class ChangeDataFolderCommandTests : CliCommandTestsBase { - public ChangeDataFolderCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Send_Change_Data_Folder_Request() { var path = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/CliCommandTestBase.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/CliCommandTestBase.cs index eaba30d833..c8e1ad7686 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/CliCommandTestBase.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/CliCommandTestBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,17 +27,18 @@ using System.Security.Cryptography.X509Certificates; using Autofac; using Catalyst.Abstractions.Cli; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.Rpc; using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Rpc; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; using FluentAssertions; using Google.Protobuf; using NSubstitute; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { @@ -53,12 +54,19 @@ public abstract class CliCommandTestsBase : FileSystemBasedTest protected ILifetimeScope Scope; protected ICatalystCli Shell; - protected CliCommandTestsBase(ITestOutputHelper output) : base(output, new[] + protected CliCommandTestsBase() : base(new[] { Path.Combine(Constants.ConfigSubFolder, TestConstants.TestShellNodesConfigFile), Path.Combine(Constants.ConfigSubFolder, CliConstants.ShellConfigFile), }) { + + } + + public override void Setup(TestContext output) + { + base.Setup(output); + ConfigureModules(); ConfigureNodeClient(); @@ -97,7 +105,7 @@ protected void ConfigureNodeClient() var nodeRpcClientFactory = Substitute.For(); nodeRpcClientFactory - .GetClient(Arg.Any(), Arg.Is(c => c.NodeId == ServerNodeName)) + .GetClientAsync(Arg.Any(), Arg.Is(c => c.NodeId == ServerNodeName)) .Returns(RpcClient); ContainerProvider.ContainerBuilder.RegisterInstance(nodeRpcClientFactory).As(); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/ConnectNodeTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/ConnectNodeTests.cs index 4cc01ccedf..865792354d 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/ConnectNodeTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/ConnectNodeTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,17 +21,23 @@ #endregion +using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class ConnectNodeTests : CliCommandTestsBase { - public ConnectNodeTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Handle_Multiple_Connection_Attempts() { for (var i = 0; i < 10; i++) diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetDeltaCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetDeltaCommandTests.cs index 69fad2062f..8023a02fb6 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetDeltaCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetDeltaCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,26 +21,32 @@ #endregion -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Protocol.Rpc.Node; +using Catalyst.TestUtils; using FluentAssertions; -using TheDotNetLeague.MultiFormats.MultiBase; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using Xunit.Abstractions; +using MultiFormats; +using MultiFormats.Registry; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class GetDeltaCommandTests : CliCommandTestsBase { - public GetDeltaCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Request_Node_Info() { - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - var hash = CidHelper.CreateCid(hashProvider.ComputeUtf8MultiHash("hello")); + var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + var hash = hashProvider.ComputeUtf8MultiHash("hello").ToCid(); var result = Shell.ParseCommand("getdelta", "-h", hash, NodeArgumentPrefix, ServerNodeName); result.Should().BeTrue(); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetFileCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetFileCommandTests.cs index e08f0b648d..1b5f2d0312 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetFileCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetFileCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,16 +26,17 @@ using System.Linq; using System.Threading.Tasks; using Autofac; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; using Catalyst.Protocol.Rpc.Node; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class GetFileCommandTests : CliCommandTestsBase { public static IEnumerable GetFileData => @@ -45,10 +46,14 @@ public sealed class GetFileCommandTests : CliCommandTestsBase {"/fake_file_hash", AppDomain.CurrentDomain.BaseDirectory + "/Config/addfile_test.json", true} }; - public GetFileCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } [Theory] - [MemberData(nameof(GetFileData))] + [TestCaseSource(nameof(GetFileData))] public async Task Cli_Can_Send_Get_File_Request(string fileHash, string outputPath, bool expectedResult) { var downloadFileFactory = Scope.Resolve(); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetInfoCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetInfoCommandTests.cs index 6bc2110287..ee1d25d1b8 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetInfoCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetInfoCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,17 +22,23 @@ #endregion using Catalyst.Protocol.Rpc.Node; +using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class GetInfoCommandTests : CliCommandTestsBase { - public GetInfoCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Request_Node_Info() { var result = Shell.ParseCommand("getinfo", NodeArgumentPrefix, ServerNodeName); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetMempoolTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetMempoolTests.cs index 780b05a944..5d3d6f60a3 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetMempoolTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetMempoolTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,17 +22,22 @@ #endregion using Catalyst.Protocol.Rpc.Node; +using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [Category(Traits.IntegrationTest)] public sealed class GetMempoolCommandTests : CliCommandTestsBase { - public GetMempoolCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Request_Node_Mempool() { var result = Shell.ParseCommand("getmempool", NodeArgumentPrefix, ServerNodeName); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetPeerInfoCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetPeerInfoCommandTests.cs index 7d88cbe4cf..55efac5f03 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetPeerInfoCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetPeerInfoCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,24 +22,28 @@ #endregion using Catalyst.Protocol.Rpc.Node; +using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class GetPeerInfoCommandTests : CliCommandTestsBase { - public GetPeerInfoCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Send_Get_Peer_Info_Request() { - var publicKey = "fake_public_key"; - var ipAddress = "127.0.0.1"; + var address = "/ip4/127.0.0.1/tcp/42066/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"; - var result = Shell.ParseCommand("getpeerinfo", NodeArgumentPrefix, ServerNodeName, "-i", ipAddress, "-p", - publicKey); + var result = Shell.ParseCommand("getpeerinfo", NodeArgumentPrefix, ServerNodeName, "-a", address); result.Should().BeTrue(); AssertSentMessageAndGetMessageContent(); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetVersionCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetVersionCommandTests.cs index 67903afa95..452db0d55d 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetVersionCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/GetVersionCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,17 +22,23 @@ #endregion using Catalyst.Protocol.Rpc.Node; +using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class GetVersionCommandTests : CliCommandTestsBase { - public GetVersionCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Request_Node_Version() { var result = Shell.ParseCommand("getversion", NodeArgumentPrefix, ServerNodeName); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/MessageSignCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/MessageSignCommandTests.cs index 8b87027230..4f4364315a 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/MessageSignCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/MessageSignCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,17 +22,23 @@ #endregion using Catalyst.Protocol.Rpc.Node; +using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class MessageSignCommandTests : CliCommandTestsBase { - public MessageSignCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Request_Node_To_Sign_A_Message() { var result = Shell.ParseCommand("sign", "-m", "test message", NodeArgumentPrefix, ServerNodeName); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/MessageVerifyCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/MessageVerifyCommandTests.cs index 771e0cc70d..0305b82e21 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/MessageVerifyCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/MessageVerifyCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,17 +22,22 @@ #endregion using Catalyst.Protocol.Rpc.Node; +using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [Category(Traits.IntegrationTest)] public sealed class MessageVerifyCommandTests : CliCommandTestsBase { - public MessageVerifyCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Verify_Message() { var result = Shell.ParseCommand( diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerCountCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerCountCommandTests.cs index 1859396242..a2d1be2cd7 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerCountCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerCountCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,17 +22,23 @@ #endregion using Catalyst.Protocol.Rpc.Node; +using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class PeerCommandCommandTests : CliCommandTestsBase { - public PeerCommandCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Send_Peers_Count_Request() { var result = Shell.ParseCommand("peercount", NodeArgumentPrefix, ServerNodeName); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerListCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerListCommandTests.cs index ca690782fe..f87b14d0e9 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerListCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerListCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,17 +22,23 @@ #endregion using Catalyst.Protocol.Rpc.Node; +using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class PeerListCommandTests : CliCommandTestsBase { - public PeerListCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Send_List_Peers_Request() { var result = Shell.ParseCommand("listpeers", NodeArgumentPrefix, ServerNodeName); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerRemoveCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerRemoveCommandTests.cs index f0e6e5dbbc..e1421b416d 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerRemoveCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerRemoveCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,21 +22,29 @@ #endregion using Catalyst.Protocol.Rpc.Node; +using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class PeerRemoveCommandTests : CliCommandTestsBase { - public PeerRemoveCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Send_Remove_Peer_Request() { + var address = "/ip4/127.0.0.1/tcp/42066/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"; + var result = Shell.ParseCommand( - "removepeer", NodeArgumentPrefix, ServerNodeName, "-p", "fake_public_key", "-i", "127.0.0.1"); + "removepeer", NodeArgumentPrefix, ServerNodeName, "-a", address); result.Should().BeTrue(); AssertSentMessageAndGetMessageContent(); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerReputationCommandTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerReputationCommandTests.cs index cb229827c4..54caade89b 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerReputationCommandTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Commands/PeerReputationCommandTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,20 +23,25 @@ using Catalyst.Protocol.Rpc.Node; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Commands { public sealed class PeerReputationCommandTests : CliCommandTestsBase { - public PeerReputationCommandTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void Cli_Can_Send_Peer_Reputation_Request() { + var address = "/ip4/127.0.0.1/tcp/42066/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"; + var result = Shell.ParseCommand( - "peerrep", NodeArgumentPrefix, ServerNodeName, "-i", "127.0.0.1", "-p", "fake_public_key"); + "peerrep", NodeArgumentPrefix, ServerNodeName, "-a", address); result.Should().BeTrue(); AssertSentMessageAndGetMessageContent(); diff --git a/src/Catalyst.Cli.Tests/IntegrationTests/Config/GlobalConfigTests.cs b/src/Catalyst.Cli.Tests/IntegrationTests/Config/GlobalConfigTests.cs index 508207d0e8..c66047685e 100644 --- a/src/Catalyst.Cli.Tests/IntegrationTests/Config/GlobalConfigTests.cs +++ b/src/Catalyst.Cli.Tests/IntegrationTests/Config/GlobalConfigTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,7 +26,6 @@ using System.Linq; using Autofac; using Catalyst.Abstractions.Cli; -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.Commands; using Catalyst.Core.Lib; using Catalyst.Core.Lib.Cli; @@ -35,24 +34,28 @@ using Catalyst.Core.Modules.KeySigner; using Catalyst.Core.Modules.Keystore; using Catalyst.Core.Modules.Rpc.Client; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Network; using Catalyst.TestUtils; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Cli.Tests.IntegrationTests.Config { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class GlobalConfigTests : FileSystemBasedTest { public static readonly List Networks = new List {NetworkType.Devnet, NetworkType.Mainnet, NetworkType.Testnet} .Select(n => new object[] {n}).ToList(); - public GlobalConfigTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Theory] - [MemberData(nameof(Networks))] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [TestCaseSource(nameof(Networks))] public void Registering_All_Configs_Should_Allow_Resolving_ICatalystCli(NetworkType network) { var configFilesUsed = new[] diff --git a/src/Catalyst.Cli.Tests/UnitTests/CatalystCliOptionsTest.cs b/src/Catalyst.Cli.Tests/UnitTests/CatalystCliOptionsTest.cs index 141ab7517b..ff67d608a3 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/CatalystCliOptionsTest.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/CatalystCliOptionsTest.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,7 +26,7 @@ using System.IO; using Catalyst.Cli.Options; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests { @@ -58,8 +58,7 @@ public sealed class CatalystCliOptionsTest public CatalystCliOptionsTest() { _changeDataFolderOptions = new ChangeDataFolderOptions(); } - [Theory] - [MemberData(nameof(WorkingPaths))] + [TestCaseSource(nameof(WorkingPaths))] public void ChangeDataFolder_Set_Data_Folder_Property_Valid_Path_Must_Store_Successfully(string path) { _changeDataFolderOptions.DataFolder = path; @@ -67,10 +66,9 @@ public void ChangeDataFolder_Set_Data_Folder_Property_Valid_Path_Must_Store_Succ _changeDataFolderOptions.DataFolder.Should().Be(path); } - [Theory] - [InlineData("'q*Pen\0'cilL:\\123\\fak / e'")] - [InlineData("'phmtbt*\0 3 / lopg'")] - [InlineData( + [TestCase("'q*Pen\0'cilL:\\123\\fak / e'")] + [TestCase("'phmtbt*\0 3 / lopg'")] + [TestCase( "'\0/gthgt5\000*\0 3 / woigwogmom4t4040gkwvkinwewowegmvowpmgopweWe will WIN anyway, but it would be much easier if the g5 undergmowgewgwgwegwegegegeg'")] public void ChangeDataFolder_Does_Not_Set_Data_Folder_Property_Invalid_Path(string path) { diff --git a/src/Catalyst.Cli.Tests/UnitTests/CatalystCliTests.cs b/src/Catalyst.Cli.Tests/UnitTests/CatalystCliTests.cs index 710c886b1e..7b82864552 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/CatalystCliTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/CatalystCliTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,17 +24,17 @@ using System.Collections.Generic; using System.Threading; using Catalyst.Abstractions.Cli; -using Catalyst.Abstractions.Cli.Commands; -using Catalyst.Abstractions.Cli.CommandTypes; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.Rpc; using Catalyst.Cli.Commands; -using Catalyst.Core.Lib.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.CommandTypes; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Rpc; using FluentAssertions; using Microsoft.Extensions.Configuration; using NSubstitute; +using NUnit.Framework; using Serilog; -using Xunit; namespace Catalyst.Cli.Tests.UnitTests { @@ -61,7 +61,7 @@ public CatalystCliTests() private readonly ICommandContext _commandContext; - [Fact] + [Test] public void ParseCommand_That_Does_Exist_Should_Return_True() { var userOutput = Substitute.For(); @@ -74,7 +74,7 @@ public void ParseCommand_That_Does_Exist_Should_Return_True() catalystCli.ParseCommand("test").Should().BeTrue(); } - [Fact] + [Test] public void ParseCommand_That_Does_Not_Exist_Should_Return_False() { var userOutput = Substitute.For(); @@ -84,7 +84,7 @@ public void ParseCommand_That_Does_Not_Exist_Should_Return_False() catalystCli.ParseCommand("test").Should().BeFalse(); } - [Fact] + [Test] public void RunConsole_Stops_On_Cancellation_Token() { var userOutput = Substitute.For(); diff --git a/src/Catalyst.Cli.Tests/UnitTests/CommandContextTests.cs b/src/Catalyst.Cli.Tests/UnitTests/CommandContextTests.cs index 6bae172b3f..ee2e6fb095 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/CommandContextTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/CommandContextTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,16 +23,16 @@ using System; using Catalyst.Abstractions.Cli; -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.Rpc; using Catalyst.Cli.Commands; -using Catalyst.Core.Lib.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Rpc; using FluentAssertions; using Microsoft.Extensions.Configuration; using NSubstitute; +using NUnit.Framework; using Serilog; -using Xunit; namespace Catalyst.Cli.Tests.UnitTests { @@ -60,41 +60,37 @@ public CommandContextTests() private readonly ICommandContext _commandContext; - [Fact] + [Test] public void GetConnectedNode_Should_Throw_ArgumentException_On_Empty_NodeId() { - var exception = Record.Exception(() => _commandContext.GetConnectedNode(string.Empty)); - exception.Should().BeOfType(); + Assert.Throws(() => _commandContext.GetConnectedNode(string.Empty)); } - [Fact] + [Test] public void GetConnectedNode_Should_Throw_ArgumentNullException_On_Null_NodeId() { - var exception = Record.Exception(() => _commandContext.GetConnectedNode(null)); - exception.Should().BeOfType(); + Assert.Throws(() => _commandContext.GetConnectedNode(null)); } - [Fact] + [Test] public void GetNodeConfig_Should_Return_Null_On_EmptyConfigs() { _commandContext.GetNodeConfig("No_node_config").Should().BeNull(); } - [Fact] + [Test] public void GetNodeConfig_Should_Throw_ArgumentException_On_Empty_NodeId() { - var exception = Record.Exception(() => _commandContext.GetNodeConfig(string.Empty)); - exception.Should().BeOfType(); + Assert.Throws(() => _commandContext.GetNodeConfig(string.Empty)); } - [Fact] + [Test] public void GetNodeConfig_Should_Throw_ArgumentNullException_On_Null_NodeId() { - var exception = Record.Exception(() => _commandContext.GetNodeConfig(null)); - exception.Should().BeOfType(); + Assert.Throws(() => _commandContext.GetNodeConfig(null)); } - [Fact] + [Test] public void IsSocketChannelActive_Should_Return_False_On_Inactive_Channel() { var rpcClient = Substitute.For(); @@ -103,7 +99,7 @@ public void IsSocketChannelActive_Should_Return_False_On_Inactive_Channel() _commandContext.IsSocketChannelActive(rpcClient).Should().BeFalse(); } - [Fact] + [Test] public void IsSocketChannelActive_Should_Return_True_On_Active_Channel() { var rpcClient = Substitute.For(); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/ConnectTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/ConnectTests.cs index 81d71a826f..0ea3202e21 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/ConnectTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/ConnectTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,6 @@ using System; using System.Collections.Generic; -using Catalyst.Abstractions.Cli.CommandTypes; using Catalyst.Abstractions.Rpc; using Catalyst.Cli.Commands; using Catalyst.Cli.Tests.UnitTests.Helpers; @@ -31,7 +30,8 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.CommandTypes; namespace Catalyst.Cli.Tests.UnitTests.Commands { @@ -42,26 +42,25 @@ public sealed class ConnectTests public ConnectTests() { _logger = Substitute.For(); } - [Fact] + [Test] public void Cannot_Connect_With_Invalid_Config() { var commandContext = TestCommandHelpers.GenerateCliCommandContext(); commandContext.GetNodeConfig(Arg.Any()).Returns((IRpcClientConfig) null); - var commands = new List {new ConnectCommand(commandContext, _logger)}; + var commands = new List { new ConnectCommand(commandContext, _logger) }; var console = new CatalystCli(commandContext.UserOutput, commands); - var exception = Record.Exception(() => console.ParseCommand("connect", "-n", "node1")); - exception.Should().BeOfType(); + var exception = Assert.Throws(() => console.ParseCommand("connect", "-n", "node1")); } - [Fact] + [Test] public void Cannot_Connect_With_Invalid_SocketChannel() { var commandContext = TestCommandHelpers.GenerateCliCommandContext(); TestCommandHelpers.MockRpcNodeConfig(commandContext); - var commands = new List {new ConnectCommand(commandContext, _logger)}; + var commands = new List { new ConnectCommand(commandContext, _logger) }; var console = new CatalystCli(commandContext.UserOutput, commands); var isCommandParsed = console.ParseCommand("connect", "-n", "test"); @@ -70,13 +69,13 @@ public void Cannot_Connect_With_Invalid_SocketChannel() commandContext.UserOutput.Received(1).WriteLine(ConnectCommand.InvalidSocketChannel); } - [Fact] + [Test] public void Connect_Should_Connect_To_Node() { var commandContext = TestCommandHelpers.GenerateCliFullCommandContext(); TestCommandHelpers.AddClientSocketRegistry(commandContext, _testScheduler); - var commands = new List {new ConnectCommand(commandContext, _logger)}; + var commands = new List { new ConnectCommand(commandContext, _logger) }; var console = new CatalystCli(commandContext.UserOutput, commands); var isCommandParsed = console.ParseCommand("connect", "-n", "test"); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/DisconnectTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/DisconnectTests.cs index 7823af106c..68294804d1 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/DisconnectTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/DisconnectTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,6 @@ #endregion using System.Collections.Generic; -using Catalyst.Abstractions.Cli.CommandTypes; using Catalyst.Cli.Commands; using Catalyst.Cli.Tests.UnitTests.Helpers; using Catalyst.Core.Lib.Network; @@ -30,7 +29,9 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.CommandTypes; +using Catalyst.Core.Lib.Extensions; namespace Catalyst.Cli.Tests.UnitTests.Commands { @@ -38,7 +39,7 @@ public sealed class DisconnectTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void Disconnect_Should_Disconnect_From_Node() { var commandContext = TestCommandHelpers.GenerateCliCommandContext(); @@ -50,8 +51,7 @@ public void Disconnect_Should_Disconnect_From_Node() var socketClientRegistry = TestCommandHelpers.AddClientSocketRegistry(commandContext, _testScheduler); var clientHashCode = - socketClientRegistry.GenerateClientHashCode( - EndpointBuilder.BuildNewEndPoint(rpcNodeConfig.HostAddress, rpcNodeConfig.Port)); + socketClientRegistry.GenerateClientHashCode(rpcNodeConfig.Address.GetIPEndPoint()); socketClientRegistry.AddClientToRegistry(clientHashCode, nodeRpcClient); var commands = new List {new DisconnectCommand(commandContext, Substitute.For())}; diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/AddFileToDfsRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/AddFileToDfsRequestTests.cs index a8858bf579..3352b40231 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/AddFileToDfsRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/AddFileToDfsRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,13 +22,13 @@ #endregion /* This class needs unit tests to test functionality */ -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class AddFileToDfsRequestTests { - [Fact] + [Test] public void AddFileToDfsRequest_Can_Be_Sent() { } } } diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetDeltaRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetDeltaRequestTests.cs index c82d75c1e5..a740b1ccfc 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetDeltaRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetDeltaRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,14 +23,14 @@ using Catalyst.Cli.Commands; using Catalyst.Cli.Tests.UnitTests.Helpers; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Protocol.Rpc.Node; using FluentAssertions; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { @@ -40,12 +40,12 @@ public sealed class GetDeltaRequestTests public GetDeltaRequestTests() { _logger = Substitute.For(); } - [Fact] + [Test] public void GetDeltaRequest_Can_Be_Sent() { //Arrange - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - var deltaMultiHash = CidHelper.CreateCid(hashProvider.ComputeUtf8MultiHash("previous")); + var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + var deltaMultiHash = hashProvider.ComputeUtf8MultiHash("previous").ToCid(); var commandContext = TestCommandHelpers.GenerateCliRequestCommandContext(); var connectedNode = commandContext.GetConnectedNode(null); var command = new GetDeltaCommand(commandContext, _logger); @@ -58,7 +58,7 @@ public void GetDeltaRequest_Can_Be_Sent() requestSent.Should().BeOfType(typeof(GetDeltaRequest)); } - [Fact] + [Test] public void GetDeltaRequest_Should_Be_Invalid_Multihash() { //Arrange diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetFileFromDfsRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetFileFromDfsRequestTests.cs index 85d09b3e22..7c70ba46c8 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetFileFromDfsRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetFileFromDfsRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetInfoRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetInfoRequestTests.cs index 182c41ba34..3df2136427 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetInfoRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetInfoRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,13 +27,13 @@ using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class GetInfoRequestTests { - [Fact] + [Test] public void GetInfoRequest_Can_Be_Sent() { //Arrange diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetMempoolRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetMempoolRequestTests.cs index 06b090354b..ffdeb1231b 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetMempoolRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetMempoolRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,13 +27,13 @@ using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class GetMempoolRequestTests { - [Fact] + [Test] public void GetMempoolRequest_Can_Be_Sent() { //Arrange diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerBlackListingRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerBlackListingRequestTests.cs index 119f6f9a98..f37cd9ffe2 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerBlackListingRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerBlackListingRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,27 +27,27 @@ using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class GetPeerBlackListingRequestTests { - [Fact] + [Test] public void GetPeerBlackListingRequest_Can_Be_Sent() { //Arrange var commandContext = TestCommandHelpers.GenerateCliRequestCommandContext(); var connectedNode = commandContext.GetConnectedNode(null); var command = new PeerBlackListingCommand(commandContext, Substitute.For()); + var address = "/ip4/127.0.0.1/tcp/42066/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"; //Act - TestCommandHelpers.GenerateRequest(commandContext, command, "-n", "node1", "-b", "true", "-i", "10.1.1.1", - "-p", "public key"); + TestCommandHelpers.GenerateRequest(commandContext, command, "-n", "node1", "-b", "true", "-a", address); //Assert - var requestSent = TestCommandHelpers.GetRequest(connectedNode); - requestSent.Should().BeOfType(typeof(SetPeerBlacklistRequest)); + var requestSent = TestCommandHelpers.GetRequest(connectedNode); + requestSent.Should().BeOfType(typeof(SetPeerBlackListRequest)); } } } diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerCountRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerCountRequestTests.cs index cb180c4c4d..f8d39808c8 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerCountRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerCountRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,13 +27,13 @@ using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class GetPeerCountRequestTests { - [Fact] + [Test] public void GetPeerCountRequest_Can_Be_Sent() { //Arrange diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerInfoRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerInfoRequestTests.cs index fa28ce9d74..cbdc089530 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerInfoRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerInfoRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,23 +27,23 @@ using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class GetPeerInfoRequestTests { - [Fact] + [Test] public void GetPeerInfoRequest_Can_Be_Sent() { //Arrange var commandContext = TestCommandHelpers.GenerateCliRequestCommandContext(); var connectedNode = commandContext.GetConnectedNode(null); var command = new GetPeerInfoCommand(commandContext, Substitute.For()); + var address = "/ip4/127.0.0.1/tcp/42066/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"; //Act - TestCommandHelpers.GenerateRequest(commandContext, command, "-n", "node1", "-i", "10.1.1.1", "-p", - "publickey"); + TestCommandHelpers.GenerateRequest(commandContext, command, "-n", "node1", "-a", address); //Assert var requestSent = TestCommandHelpers.GetRequest(connectedNode); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerListRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerListRequestTests.cs index a365629774..a210f3fe11 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerListRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerListRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,13 +27,13 @@ using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class GetPeerListRequestTests { - [Fact] + [Test] public void GetPeerListResponse_Can_Be_Sent() { //Arrange diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerReputationRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerReputationRequestTests.cs index aeed0bd95b..ced7f819f1 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerReputationRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetPeerReputationRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,23 +27,23 @@ using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class GetPeerReputationRequestTests { - [Fact] + [Test] public void GetPeerReputation_Can_Be_Sent() { //Arrange var commandContext = TestCommandHelpers.GenerateCliRequestCommandContext(); var connectedNode = commandContext.GetConnectedNode(null); var command = new PeerReputationCommand(commandContext, Substitute.For()); + var address = "/ip4/127.0.0.1/tcp/42066/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"; //Act - TestCommandHelpers.GenerateRequest(commandContext, command, "-n", "node1", "-i", "10.1.1.1", "-p", - "public key"); + TestCommandHelpers.GenerateRequest(commandContext, command, "-n", "node1", "-a", address); //Assert var requestSent = TestCommandHelpers.GetRequest(connectedNode); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetVersionRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetVersionRequestTests.cs index cebbc96fd4..14b38037c6 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetVersionRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/GetVersionRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,13 +27,13 @@ using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class GetVersionRequestTests { - [Fact] + [Test] public void GetVersionRequest_Can_Be_Sent() { //Arrange diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/RemovePeerRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/RemovePeerRequestTests.cs index 01035e71e6..f900525c6f 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/RemovePeerRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/RemovePeerRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,23 +27,23 @@ using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class RemovePeerRequestTests { - [Fact] + [Test] public void RemovePeerRequest_Can_Be_Sent() { //Arrange var commandContext = TestCommandHelpers.GenerateCliRequestCommandContext(); var connectedNode = commandContext.GetConnectedNode(null); var command = new PeerRemoveCommand(commandContext, Substitute.For()); + var address = "/ip4/127.0.0.1/tcp/42066/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"; //Act - TestCommandHelpers.GenerateRequest(commandContext, command, "-n", "node1", "-i", "10.1.1.1", "-p", - "publickey"); + TestCommandHelpers.GenerateRequest(commandContext, command, "-n", "node1", "-a", address); //Assert var requestSent = TestCommandHelpers.GetRequest(connectedNode); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/SignMessageRequestTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/SignMessageRequestTests.cs index 47db7b659e..06970bc4ac 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/SignMessageRequestTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/SignMessageRequestTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,13 +27,13 @@ using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class SignMessageRequestTests { - [Fact] + [Test] public void SignMessageRequest_Can_Be_Sent() { //Arrange diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/VerifyMessageResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/VerifyMessageResponseTests.cs index 4cc1a14062..f0132d105c 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/VerifyMessageResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Request/VerifyMessageResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,13 +27,13 @@ using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Request { public sealed class VerifyMessageResponseTests { - [Fact] + [Test] public void VerifyMessageRequest_Can_Be_Sent() { //Arrange diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/AddFileToDfsResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/AddFileToDfsResponseTests.cs index dbd76d7e5d..76cb1e3f84 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/AddFileToDfsResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/AddFileToDfsResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,7 +29,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -40,7 +40,7 @@ public sealed class AddFileToDfsResponseTests public AddFileToDfsResponseTests() { _logger = Substitute.For(); } - [Fact] + [Test] public void AddFileToDfsResponse_Failed_Can_Get_Output() { //Arrange @@ -50,7 +50,7 @@ public void AddFileToDfsResponse_Failed_Can_Get_Output() }; var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var addFileToDfsCommand = new AddFileCommand(null, commandContext, _logger); + new AddFileCommand(null, commandContext, _logger); //Act TestCommandHelpers.GenerateResponse(commandContext, addFileToDfsResponse); @@ -63,7 +63,7 @@ public void AddFileToDfsResponse_Failed_Can_Get_Output() addFileToDfsResponse.DfsHash); } - [Fact] + [Test] public void AddFileToDfsResponse_Finished_Can_Get_Output() { //Arrange @@ -73,7 +73,7 @@ public void AddFileToDfsResponse_Finished_Can_Get_Output() }; var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var addFileToDfsCommand = new AddFileCommand(null, commandContext, _logger); + new AddFileCommand(null, commandContext, _logger); //Act TestCommandHelpers.GenerateResponse(commandContext, addFileToDfsResponse); @@ -86,13 +86,13 @@ public void AddFileToDfsResponse_Finished_Can_Get_Output() " Dfs Hash: " + addFileToDfsResponse.DfsHash); } - [Fact] + [Test] public void AddFileToDfsResponse_No_Response_Codes() { //Arrange var addFileToDfsResponse = new AddFileToDfsResponse(); var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var addFileToDfsCommand = new AddFileCommand(null, commandContext, _logger); + new AddFileCommand(null, commandContext, _logger); //Act TestCommandHelpers.GenerateResponse(commandContext, addFileToDfsResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetDeltaResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetDeltaResponseTests.cs index b1352a275c..061d22fcb6 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetDeltaResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetDeltaResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,7 +29,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -40,13 +40,13 @@ public sealed class GetDeltaResponseTests public GetDeltaResponseTests() { _logger = Substitute.For(); } - [Fact] + [Test] public void GetDeltaResponse_Can_Get_Output() { //Arrange var deltaResponse = new GetDeltaResponse {Delta = new Delta()}; var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var getDeltaCommand = new GetDeltaCommand(commandContext, _logger); + new GetDeltaCommand(commandContext, _logger); //Act TestCommandHelpers.GenerateResponse(commandContext, deltaResponse); @@ -57,13 +57,13 @@ public void GetDeltaResponse_Can_Get_Output() commandContext.UserOutput.Received(1).WriteLine(deltaResponse.Delta.ToJsonString()); } - [Fact] + [Test] public void GetDeltaResponse_Error_On_Null_Delta() { //Arrange var deltaResponse = new GetDeltaResponse(); var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var getDeltaCommand = new GetDeltaCommand(commandContext, _logger); + new GetDeltaCommand(commandContext, _logger); //Act TestCommandHelpers.GenerateResponse(commandContext, deltaResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetFileFromDfsResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetFileFromDfsResponseTests.cs index df25655809..665f24cb5e 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetFileFromDfsResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetFileFromDfsResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -36,13 +36,13 @@ public sealed class GetFileFromDfsResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void GetFileFromDfsResponse_Can_Get_Output() { //Arrange var getFileFromDfsResponse = new GetFileFromDfsResponse(); var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var getFileFromDfsCommand = new GetFileCommand(null, commandContext, Substitute.For()); + new GetFileCommand(null, commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, getFileFromDfsResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetInfoResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetInfoResponseTests.cs index d793c8b791..9d15c2cb27 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetInfoResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetInfoResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -36,13 +36,13 @@ public sealed class GetInfoResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void GetInfoResponse_Can_Get_Output() { //Arrange var getInfoResponse = new GetInfoResponse {Query = "Test"}; var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var getInfoCommand = new GetInfoCommand(commandContext, Substitute.For()); + new GetInfoCommand(commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, getInfoResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetMempoolResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetMempoolResponseTests.cs index f1d0733bb1..e250b8d1ec 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetMempoolResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetMempoolResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -36,13 +36,13 @@ public sealed class GetMempoolResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void GetMempoolResponse_Can_Get_Output() { //Arrange var getMempoolResponse = new GetMempoolResponse(); var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var getMempoolCommand = new GetMempoolCommand(commandContext, Substitute.For()); + new GetMempoolCommand(commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, getMempoolResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerBlackListingResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerBlackListingResponseTests.cs index 6b779dcb8d..a66072ccf7 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerBlackListingResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerBlackListingResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -36,13 +36,13 @@ public sealed class GetPeerBlackListingResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void GetPeerBlackListingResponse_Can_Get_Output() { //Arrange - var setPeerBlackListRequest = new SetPeerBlacklistResponse(); + var setPeerBlackListRequest = new SetPeerBlackListResponse(); var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var getPeerBlackListingCommand = new PeerBlackListingCommand(commandContext, Substitute.For()); + new PeerBlackListingCommand(commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, setPeerBlackListRequest); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerCountResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerCountResponseTests.cs index 4263c36817..e67e16fd25 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerCountResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerCountResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -36,13 +36,13 @@ public sealed class GetPeerCountResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void GetPeerCountResponse_Can_Get_Output() { //Arrange var getPeerCountResponse = new GetPeerCountResponse {PeerCount = 50}; var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var getPeerCountCommand = new PeerCountCommand(commandContext, Substitute.For()); + new PeerCountCommand(commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, getPeerCountResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerInfoResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerInfoResponseTests.cs index 96213c7605..ff8de29712 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerInfoResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerInfoResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,7 +29,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -37,7 +37,7 @@ public sealed class GetPeerInfoResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void GetPeerInfoResponse_Can_Get_Output() { //Arrange @@ -46,7 +46,7 @@ public void GetPeerInfoResponse_Can_Get_Output() getPeerInfoResponse.PeerInfo.Add(peer); var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var getPeerInfoCommand = new GetPeerInfoCommand(commandContext, Substitute.For()); + new GetPeerInfoCommand(commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, getPeerInfoResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerListResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerListResponseTests.cs index 5b2608a2d9..d34a71ca92 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerListResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerListResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,7 +29,8 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.TestUtils; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -37,15 +38,15 @@ public sealed class GetPeerListResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void GetPeerListResponse_Can_Get_Output() { var getPeerListResponse = new GetPeerListResponse(); - getPeerListResponse.Peers.Add(new PeerId()); + getPeerListResponse.Peers.Add(MultiAddressHelper.GetAddress().ToString()); var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var getPeerListCommand = new PeerListCommand(commandContext, Substitute.For()); + new PeerListCommand(commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, getPeerListResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerReputationResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerReputationResponseTests.cs index 3c4a09eac9..0568baf30f 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerReputationResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetPeerReputationResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -39,13 +39,13 @@ public sealed class GetPeerReputationResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void GetPeerReputationResponse_Can_Get_Output() { //Arrange var getPeerReputationResponse = new GetPeerReputationResponse {Reputation = 100}; var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var getPeerReputationCommand = new PeerReputationCommand(commandContext, Substitute.For()); + new PeerReputationCommand(commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, getPeerReputationResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetVersionResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetVersionResponseTests.cs index f8a8ba97ee..59fc1ba549 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetVersionResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/GetVersionResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -36,13 +36,13 @@ public sealed class GetVersionResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void GetVersionResponse_Can_Get_Output() { //Arrange var versionResponse = new VersionResponse {Version = "1.2.3.4"}; var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var getVersionCommand = new GetVersionCommand(commandContext, Substitute.For()); + new GetVersionCommand(commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, versionResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/RemovePeerResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/RemovePeerResponseTests.cs index 8bca2a09fc..452a037838 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/RemovePeerResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/RemovePeerResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -36,13 +36,13 @@ public sealed class RemovePeerResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void RemovePeerResponse_Can_Get_Output() { //Arrange var removePeerResponse = new RemovePeerResponse(); var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var removePeerCommand = new PeerRemoveCommand(commandContext, Substitute.For()); + new PeerRemoveCommand(commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, removePeerResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/SignMessageResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/SignMessageResponseTests.cs index 0560fb7de6..c5f7ace388 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/SignMessageResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/SignMessageResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -36,13 +36,13 @@ public sealed class SignMessageResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void SignMessageResponse_Can_Get_Output() { //Arrange var signMessageResponse = new SignMessageResponse(); var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var messageSignCommand = new MessageSignCommand(commandContext, Substitute.For()); + new MessageSignCommand(commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, signMessageResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/VerifyMessageResponseTests.cs b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/VerifyMessageResponseTests.cs index 131d03bc77..8af03589d8 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/VerifyMessageResponseTests.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Commands/Response/VerifyMessageResponseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Cli.Tests.UnitTests.Commands.Response { @@ -36,13 +36,13 @@ public sealed class VerifyMessageResponseTests { private readonly TestScheduler _testScheduler = new TestScheduler(); - [Fact] + [Test] public void VerifyMessageResponse_Can_Get_Output() { //Arrange var verifyMessageResponse = new VerifyMessageResponse(); var commandContext = TestCommandHelpers.GenerateCliResponseCommandContext(_testScheduler); - var messageVerifyCommand = new MessageVerifyCommand(commandContext, Substitute.For()); + new MessageVerifyCommand(commandContext, Substitute.For()); //Act TestCommandHelpers.GenerateResponse(commandContext, verifyMessageResponse); diff --git a/src/Catalyst.Cli.Tests/UnitTests/Helpers/TestCommandHelpers.cs b/src/Catalyst.Cli.Tests/UnitTests/Helpers/TestCommandHelpers.cs index aec03fd9b4..b642cc008b 100644 --- a/src/Catalyst.Cli.Tests/UnitTests/Helpers/TestCommandHelpers.cs +++ b/src/Catalyst.Cli.Tests/UnitTests/Helpers/TestCommandHelpers.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,21 +27,22 @@ using System.Reactive.Concurrency; using System.Security.Cryptography.X509Certificates; using Catalyst.Abstractions.Cli; -using Catalyst.Abstractions.Cli.Commands; -using Catalyst.Abstractions.Cli.CommandTypes; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.Hashing; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Transport; using Catalyst.Abstractions.Rpc; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Transport; using Catalyst.Core.Modules.Hashing; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.CommandTypes; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Rpc; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using Google.Protobuf; +using MultiFormats.Registry; using NSubstitute; -using TheDotNetLeague.MultiFormats.MultiHash; namespace Catalyst.Cli.Tests.UnitTests.Helpers { @@ -64,8 +65,7 @@ public static ICommandContext GenerateCliFullCommandContext() commandContext.RpcClientFactory.Returns(nodeRpcClientFactory); commandContext.CertificateStore.Returns(certificateStore); - commandContext.PeerId.Returns( - "hv6vvbt2u567syz5labuqnfabsc3zobfwekl4cy3c574n6vkj7sq".BuildPeerIdFromBase32Key(IPAddress.Any, 9010)); + commandContext.Address.Returns("/ip4/192.168.0.181/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"); var nodeRpcClient = MockNodeRpcClient(); MockRpcNodeConfig(commandContext); @@ -86,10 +86,10 @@ public static ICommandContext GenerateCliCommandContext() commandContext.RpcClientFactory.Returns(nodeRpcClientFactory); commandContext.CertificateStore.Returns(certificateStore); - IHashProvider hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + IHashProvider hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); var deltaMultiHash = hashProvider.ComputeUtf8MultiHash("previous"); - commandContext.PeerId.Returns( - PeerIdHelper.GetPeerId(deltaMultiHash.Digest, IPAddress.Any, 9010)); + commandContext.Address.Returns( + MultiAddressHelper.GetAddress(deltaMultiHash.Digest, IPAddress.Any, 9010)); return commandContext; } @@ -98,9 +98,7 @@ public static IRpcClientConfig MockRpcNodeConfig(ICommandContext commandContext) { var rpcNodeConfig = Substitute.For(); rpcNodeConfig.NodeId = "test"; - rpcNodeConfig.HostAddress = IPAddress.Any; - rpcNodeConfig.PublicKey = "hv6vvbt2u567syz5labuqnfabsc3zobfwekl4cy3c574n6vkj7sq"; - rpcNodeConfig.Port = 9000; + rpcNodeConfig.Address = "/ip4/192.168.0.181/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"; commandContext.GetNodeConfig(Arg.Any()).Returns(rpcNodeConfig); return rpcNodeConfig; } @@ -108,7 +106,7 @@ public static IRpcClientConfig MockRpcNodeConfig(ICommandContext commandContext) public static IRpcClientFactory MockNodeRpcClientFactory(ICommandContext commandContext, IRpcClient rpcClient) { - commandContext.RpcClientFactory.GetClient(Arg.Any(), Arg.Any()) + commandContext.RpcClientFactory.GetClientAsync(Arg.Any(), Arg.Any()) .Returns(rpcClient); return commandContext.RpcClientFactory; } diff --git a/src/Catalyst.Cli/Catalyst.Cli.csproj b/src/Catalyst.Cli/Catalyst.Cli.csproj index f43a6733c9..557c6612f8 100644 --- a/src/Catalyst.Cli/Catalyst.Cli.csproj +++ b/src/Catalyst.Cli/Catalyst.Cli.csproj @@ -1,21 +1,21 @@ - + - netcoreapp3.0 + net6.0 Catalyst CLI - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) CLI tool to interact with Catalyst Nodes via RPC - Copyright © 2019 AtlasCity.io + Copyright © 2022 catalystnet.org Exe true Ctalyst.Cli.snk true + + 1701;1702;CS8002 + - - - - + @@ -25,8 +25,10 @@ + + diff --git a/src/Catalyst.Cli/CatalystCli.cs b/src/Catalyst.Cli/CatalystCli.cs index 2f3b836a90..cee96a3116 100644 --- a/src/Catalyst.Cli/CatalystCli.cs +++ b/src/Catalyst.Cli/CatalystCli.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,7 +25,7 @@ using System.Collections.Generic; using System.Linq; using Catalyst.Abstractions.Cli; -using Catalyst.Abstractions.Cli.CommandTypes; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.CommandTypes; using CommandLine; namespace Catalyst.Cli diff --git a/src/Catalyst.Cli/CatalystCliBase.cs b/src/Catalyst.Cli/CatalystCliBase.cs index f528dbc269..a3344bacee 100644 --- a/src/Catalyst.Cli/CatalystCliBase.cs +++ b/src/Catalyst.Cli/CatalystCliBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,26 +22,26 @@ #endregion using System; -using System.Globalization; using System.Reflection; using System.Text; using System.Text.RegularExpressions; using System.Threading; using Autofac; using Catalyst.Abstractions.Cli; -using Catalyst.Abstractions.Cli.Commands; -using Catalyst.Abstractions.Cli.CommandTypes; -using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.IO.Transport; -using Catalyst.Abstractions.Rpc; using Catalyst.Cli.Commands; using Catalyst.Core.Lib; using Catalyst.Core.Lib.Cli; -using Catalyst.Core.Lib.IO.Transport; using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.Core.Modules.KeySigner; using Catalyst.Core.Modules.Keystore; using Catalyst.Core.Modules.Rpc.Client; +using Catalyst.Modules.Network.Dotnetty; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.CommandTypes; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Rpc; namespace Catalyst.Cli { @@ -54,7 +54,6 @@ public abstract class CatalystCliBase private static string Prompt => "Koopa"; private static string ServiceName => "Catalyst Distributed Shell"; - private static CultureInfo AppCulture => new CultureInfo("en-GB", false); /// public bool RunConsole(CancellationToken ct) @@ -104,7 +103,7 @@ public static void RegisterClientDependencies(ContainerBuilder containerBuilder) containerBuilder.RegisterType().As(); containerBuilder.RegisterType().As(); containerBuilder.RegisterType().As(); - var socketClientRegistry = new SocketClientRegistry(); + SocketClientRegistry socketClientRegistry = new(); containerBuilder.RegisterInstance(socketClientRegistry).As>(); containerBuilder.RegisterType().As(); @@ -114,6 +113,8 @@ public static void RegisterClientDependencies(ContainerBuilder containerBuilder) containerBuilder.RegisterAssemblyTypes(typeof(RpcClientModule).Assembly) .AssignableTo().As() .PublicOnly(); + + containerBuilder.RegisterModule(new DotnettyNetworkModule()); } public static void RegisterCoreModules(ContainerBuilder containerBuilder) diff --git a/src/Catalyst.Cli/CliConfigCopier.cs b/src/Catalyst.Cli/CliConfigCopier.cs index ecd986f4d5..e24eb1e3d8 100644 --- a/src/Catalyst.Cli/CliConfigCopier.cs +++ b/src/Catalyst.Cli/CliConfigCopier.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/CliConstants.cs b/src/Catalyst.Cli/CliConstants.cs index 98e1260b16..f5f44ac9fa 100644 --- a/src/Catalyst.Cli/CliConstants.cs +++ b/src/Catalyst.Cli/CliConstants.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/CommandTypes/BaseCommand.cs b/src/Catalyst.Cli/CommandTypes/BaseCommand.cs index 8de6e70863..7ab64065d7 100644 --- a/src/Catalyst.Cli/CommandTypes/BaseCommand.cs +++ b/src/Catalyst.Cli/CommandTypes/BaseCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,9 +23,9 @@ using System; using System.Reflection; -using Catalyst.Abstractions.Cli.Commands; -using Catalyst.Abstractions.Cli.CommandTypes; using Catalyst.Abstractions.Cli.Options; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.CommandTypes; using CommandLine; using Serilog; diff --git a/src/Catalyst.Cli/CommandTypes/BaseMessageCommand.cs b/src/Catalyst.Cli/CommandTypes/BaseMessageCommand.cs index 4d8afc3234..2bff10c03c 100644 --- a/src/Catalyst.Cli/CommandTypes/BaseMessageCommand.cs +++ b/src/Catalyst.Cli/CommandTypes/BaseMessageCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,15 +24,15 @@ using System; using System.Collections.Concurrent; using System.Reactive.Linq; -using Catalyst.Abstractions.Cli.Commands; -using Catalyst.Abstractions.Cli.CommandTypes; using Catalyst.Abstractions.Cli.Options; -using Catalyst.Abstractions.Rpc; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Events; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.CommandTypes; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Rpc; using Google.Protobuf; +using MultiFormats; using Serilog; namespace Catalyst.Cli.CommandTypes @@ -46,7 +46,7 @@ public abstract class BaseMessageCommand : BaseCom private readonly IDisposable _eventStreamObserverClientAdded; private readonly IDisposable _eventStreamObserverClientRemoved; private readonly ConcurrentDictionary _subscriptions; - private PeerId _recipientPeerId; + private MultiAddress _recipientAddress; protected BaseMessageCommand(ICommandContext commandContext, ILogger logger) : base(commandContext, logger) @@ -58,20 +58,22 @@ protected BaseMessageCommand(ICommandContext commandContext, ILogger logger) .OfType().Subscribe(SocketClientRegistryClientRemovedOnNext); } - protected PeerId RecipientPeerId + protected MultiAddress RecipientAddress { get { - if (_recipientPeerId != null) return _recipientPeerId; + if (_recipientAddress != null) + { + return _recipientAddress; + } + var rpcClientConfig = CommandContext.GetNodeConfig(Options.Node); - _recipientPeerId = - rpcClientConfig.PublicKey.BuildPeerIdFromBase32Key(rpcClientConfig.HostAddress, - rpcClientConfig.Port); - return _recipientPeerId; + _recipientAddress = rpcClientConfig.Address; + return _recipientAddress; } } - protected PeerId SenderPeerId => CommandContext.PeerId; + protected MultiAddress SenderAddress => CommandContext.Address; public void Dispose() { Dispose(true); } @@ -83,9 +85,9 @@ public virtual void SendMessage(TOption options) if (message == null) return; - var messageDto = new MessageDto( - message.ToProtocolMessage(SenderPeerId), - RecipientPeerId); + MessageDto messageDto = new( + message.ToProtocolMessage(SenderAddress), + RecipientAddress); Target.SendMessage(messageDto); } diff --git a/src/Catalyst.Cli/Commands/AddFileCommand.cs b/src/Catalyst.Cli/Commands/AddFileCommand.cs index 6f764d43cb..d13196d57e 100644 --- a/src/Catalyst.Cli/Commands/AddFileCommand.cs +++ b/src/Catalyst.Cli/Commands/AddFileCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,14 +23,14 @@ using System.IO; using System.Threading; -using Catalyst.Abstractions.Cli.Commands; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.Types; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.FileTransfer; -using Catalyst.Core.Lib.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; using Catalyst.Protocol.Rpc.Node; using Serilog; @@ -67,16 +67,16 @@ public override void SendMessage(AddFileOptions options) request.FileSize = (ulong) fileStream.Length; } - var protocolMessage = request.ToProtocolMessage(SenderPeerId); - var requestMessage = new MessageDto( + var protocolMessage = request.ToProtocolMessage(SenderAddress); + MessageDto requestMessage = new( protocolMessage, - RecipientPeerId + RecipientAddress ); IUploadFileInformation fileTransfer = new UploadFileTransferInformation( File.Open(options.File, FileMode.Open), - SenderPeerId, - RecipientPeerId, + SenderAddress, + RecipientAddress, Target.Channel, requestMessage.CorrelationId); diff --git a/src/Catalyst.Cli/Commands/ChangeDataFolderCommand.cs b/src/Catalyst.Cli/Commands/ChangeDataFolderCommand.cs index 573e439613..d5cbb06924 100644 --- a/src/Catalyst.Cli/Commands/ChangeDataFolderCommand.cs +++ b/src/Catalyst.Cli/Commands/ChangeDataFolderCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,9 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; using Serilog; diff --git a/src/Catalyst.Cli/Commands/CommandContext.cs b/src/Catalyst.Cli/Commands/CommandContext.cs index 90bfffae66..b6f0a868e3 100644 --- a/src/Catalyst.Cli/Commands/CommandContext.cs +++ b/src/Catalyst.Cli/Commands/CommandContext.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,19 +24,17 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Net; using Catalyst.Abstractions.Cli; -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.IO.Transport; using Catalyst.Abstractions.Rpc; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Network; -using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Rpc.Client; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Rpc; using Dawn; using Microsoft.Extensions.Configuration; +using MultiFormats; using Serilog; namespace Catalyst.Cli.Commands @@ -59,13 +57,13 @@ public CommandContext(IConfigurationRoot config, _rpcNodeConfigs = RpcClientSettings.BuildRpcNodeSettingList(config); SocketClientRegistry = socketClientRegistry; - PeerId = GetPeerIdentifierFromCliConfig(config); + Address = GetPeerIdentifierFromCliConfig(config); RpcClientFactory = rpcClientFactory; CertificateStore = certificateStore; UserOutput = userOutput; } - public PeerId PeerId { get; } + public MultiAddress Address { get; } public IRpcClientFactory RpcClientFactory { get; } @@ -82,8 +80,7 @@ public IRpcClient GetConnectedNode(string nodeId) var nodeConfig = _rpcNodeConfigs.SingleOrDefault(node => node.NodeId.Equals(nodeId)); Guard.Argument(nodeConfig, nameof(nodeConfig)).NotNull(); - var registryId = SocketClientRegistry.GenerateClientHashCode( - EndpointBuilder.BuildNewEndPoint(nodeConfig?.HostAddress, nodeConfig.Port)); + var registryId = SocketClientRegistry.GenerateClientHashCode(nodeConfig.Address.GetIPEndPoint()); var nodeRpcClient = SocketClientRegistry.GetClientFromRegistry(registryId); Guard.Argument(nodeRpcClient).Require(IsSocketChannelActive(nodeRpcClient)); @@ -121,13 +118,11 @@ public bool IsSocketChannelActive(IRpcClient node) } } - private PeerId GetPeerIdentifierFromCliConfig(IConfigurationRoot configRoot) + private MultiAddress GetPeerIdentifierFromCliConfig(IConfigurationRoot configRoot) { var cliSettings = configRoot.GetSection("CatalystCliConfig"); - var publicKey = cliSettings.GetSection("PublicKey").Value.KeyToBytes(); - var ipAddress = IPAddress.Parse(cliSettings.GetSection("BindAddress").Value); - var port = int.Parse(cliSettings.GetSection("Port").Value); - return publicKey.BuildPeerIdFromPublicKey(ipAddress, port); + var address = cliSettings.GetSection("Address").Value; + return new MultiAddress(address); } } } diff --git a/src/Catalyst.Cli/Commands/ConnectCommand.cs b/src/Catalyst.Cli/Commands/ConnectCommand.cs index 2527251b7e..bb31274ba9 100644 --- a/src/Catalyst.Cli/Commands/ConnectCommand.cs +++ b/src/Catalyst.Cli/Commands/ConnectCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; -using Catalyst.Core.Lib.Network; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Dawn; using Serilog; @@ -43,7 +43,7 @@ protected override bool ExecuteCommand(ConnectOptions option) Guard.Argument(rpcNodeConfigs, nameof(rpcNodeConfigs)).NotNull(); //Connect to the node and store it in the socket client registry - var nodeRpcClient = CommandContext.RpcClientFactory.GetClient( + var nodeRpcClient = CommandContext.RpcClientFactory.GetClientAsync( CommandContext.CertificateStore.ReadOrCreateCertificateFile(rpcNodeConfigs.PfxFileName), rpcNodeConfigs).ConfigureAwait(false).GetAwaiter().GetResult(); @@ -53,8 +53,7 @@ protected override bool ExecuteCommand(ConnectOptions option) return false; } - var clientHashCode = CommandContext.SocketClientRegistry.GenerateClientHashCode( - EndpointBuilder.BuildNewEndPoint(rpcNodeConfigs.HostAddress, rpcNodeConfigs.Port)); + var clientHashCode = CommandContext.SocketClientRegistry.GenerateClientHashCode(rpcNodeConfigs.Address.GetIPEndPoint()); CommandContext.SocketClientRegistry.AddClientToRegistry(clientHashCode, nodeRpcClient); CommandContext.UserOutput.WriteLine($"Connected to Node {nodeRpcClient.Channel.RemoteAddress}"); diff --git a/src/Catalyst.Cli/Commands/DisconnectCommand.cs b/src/Catalyst.Cli/Commands/DisconnectCommand.cs index e70821e71b..9267ea5052 100644 --- a/src/Catalyst.Cli/Commands/DisconnectCommand.cs +++ b/src/Catalyst.Cli/Commands/DisconnectCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; -using Catalyst.Core.Lib.Network; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Dawn; using Serilog; @@ -40,8 +40,7 @@ protected override bool ExecuteCommand(DisconnectOptions option) var nodeConfig = CommandContext.GetNodeConfig(option.Node); Guard.Argument(nodeConfig, nameof(nodeConfig)).NotNull(); - var registryId = CommandContext.SocketClientRegistry.GenerateClientHashCode( - EndpointBuilder.BuildNewEndPoint(nodeConfig.HostAddress, nodeConfig.Port)); + var registryId = CommandContext.SocketClientRegistry.GenerateClientHashCode(nodeConfig.Address.GetIPEndPoint()); var node = CommandContext.SocketClientRegistry.GetClientFromRegistry(registryId); Guard.Argument(node, nameof(node)).Require(CommandContext.IsSocketChannelActive(node)); diff --git a/src/Catalyst.Cli/Commands/GetDeltaCommand.cs b/src/Catalyst.Cli/Commands/GetDeltaCommand.cs index 62158115a5..7b855eb08e 100644 --- a/src/Catalyst.Cli/Commands/GetDeltaCommand.cs +++ b/src/Catalyst.Cli/Commands/GetDeltaCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,15 +22,14 @@ #endregion using System; -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; +using MultiFormats; using Serilog; -using TheDotNetLeague.MultiFormats.MultiBase; -using TheDotNetLeague.MultiFormats.MultiHash; namespace Catalyst.Cli.Commands { @@ -45,13 +44,13 @@ protected override GetDeltaRequest GetMessage(GetDeltaOptions option) try { var hashBytes = MultiBase.Decode(option.Hash); - var cid = CidHelper.Cast(hashBytes); - var multiHash = new MultiHash(cid.Hash.ToArray()); + var cid = hashBytes.ToCid(); + new MultiHash(cid.Hash.ToArray()); return new GetDeltaRequest {DeltaDfsHash = hashBytes.ToByteString()}; } catch (FormatException fe) { - Log.Warning("Unable to parse hash {0} as a Cid", option.Hash); + Log.Warning("Unable to parse hash {0} as a Cid {1}", option.Hash, fe); CommandContext.UserOutput.WriteLine($"Unable to parse hash {option.Hash} as a Cid"); return default; } diff --git a/src/Catalyst.Cli/Commands/GetFileCommand.cs b/src/Catalyst.Cli/Commands/GetFileCommand.cs index ea42faf6fe..62eb743443 100644 --- a/src/Catalyst.Cli/Commands/GetFileCommand.cs +++ b/src/Catalyst.Cli/Commands/GetFileCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,13 +22,13 @@ #endregion using System.Threading; -using Catalyst.Abstractions.Cli.Commands; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.FileTransfer; -using Catalyst.Core.Lib.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; using Catalyst.Protocol.Rpc.Node; using Serilog; @@ -57,16 +57,16 @@ protected override GetFileFromDfsRequest GetMessage(GetFileOptions option) public override void SendMessage(GetFileOptions opts) { var message = GetMessage(opts); - var protocolMessage = message.ToProtocolMessage(SenderPeerId); + var protocolMessage = message.ToProtocolMessage(SenderAddress); var correlationId = protocolMessage.CorrelationId.ToCorrelationId(); - var messageDto = new MessageDto( + MessageDto messageDto = new( protocolMessage, - RecipientPeerId); + RecipientAddress); - var fileTransfer = new DownloadFileTransferInformation( - SenderPeerId, - RecipientPeerId, + DownloadFileTransferInformation fileTransfer = new( + SenderAddress, + RecipientAddress, Target.Channel, correlationId, opts.FileOutput, diff --git a/src/Catalyst.Cli/Commands/GetInfoCommand.cs b/src/Catalyst.Cli/Commands/GetInfoCommand.cs index 9b00953a56..ee99867a74 100644 --- a/src/Catalyst.Cli/Commands/GetInfoCommand.cs +++ b/src/Catalyst.Cli/Commands/GetInfoCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,9 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; using Serilog; diff --git a/src/Catalyst.Cli/Commands/GetMempoolCommand.cs b/src/Catalyst.Cli/Commands/GetMempoolCommand.cs index 4ae592c6fc..4ed8802b90 100644 --- a/src/Catalyst.Cli/Commands/GetMempoolCommand.cs +++ b/src/Catalyst.Cli/Commands/GetMempoolCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,9 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; using Serilog; diff --git a/src/Catalyst.Cli/Commands/GetPeerInfoCommand.cs b/src/Catalyst.Cli/Commands/GetPeerInfoCommand.cs index 3159c11868..c192f11d4c 100644 --- a/src/Catalyst.Cli/Commands/GetPeerInfoCommand.cs +++ b/src/Catalyst.Cli/Commands/GetPeerInfoCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,9 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; -using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; using Serilog; @@ -40,8 +39,7 @@ protected override GetPeerInfoRequest GetMessage(GetPeerInfoOptions option) { return new GetPeerInfoRequest { - PublicKey = option.PublicKey.ToUtf8ByteString(), - Ip = option.IpAddress.ToUtf8ByteString() + Address = option.Address }; } } diff --git a/src/Catalyst.Cli/Commands/GetVersionCommand.cs b/src/Catalyst.Cli/Commands/GetVersionCommand.cs index f1e751069e..23bc5ec26f 100644 --- a/src/Catalyst.Cli/Commands/GetVersionCommand.cs +++ b/src/Catalyst.Cli/Commands/GetVersionCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,9 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; using Serilog; diff --git a/src/Catalyst.Cli/Commands/MessageSignCommand.cs b/src/Catalyst.Cli/Commands/MessageSignCommand.cs index be0e1495f6..517294974f 100644 --- a/src/Catalyst.Cli/Commands/MessageSignCommand.cs +++ b/src/Catalyst.Cli/Commands/MessageSignCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,10 +22,10 @@ #endregion using System.Text; -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; using Google.Protobuf; using Serilog; diff --git a/src/Catalyst.Cli/Commands/MessageVerifyCommand.cs b/src/Catalyst.Cli/Commands/MessageVerifyCommand.cs index aa70378ff6..564a501ff6 100644 --- a/src/Catalyst.Cli/Commands/MessageVerifyCommand.cs +++ b/src/Catalyst.Cli/Commands/MessageVerifyCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; using Serilog; diff --git a/src/Catalyst.Cli/Commands/PeerBlackListingCommand.cs b/src/Catalyst.Cli/Commands/PeerBlackListingCommand.cs index 9aa37fc190..1289522947 100644 --- a/src/Catalyst.Cli/Commands/PeerBlackListingCommand.cs +++ b/src/Catalyst.Cli/Commands/PeerBlackListingCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,27 +21,25 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; -using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; using Serilog; namespace Catalyst.Cli.Commands { - public sealed class PeerBlackListingCommand : BaseMessageCommand { public PeerBlackListingCommand(ICommandContext commandContext, ILogger logger) : base(commandContext, logger) { } - protected override SetPeerBlacklistRequest GetMessage(PeerBlackListingOptions option) + protected override SetPeerBlackListRequest GetMessage(PeerBlackListingOptions option) { - return new SetPeerBlacklistRequest + return new SetPeerBlackListRequest { - PublicKey = option.PublicKey.ToUtf8ByteString(), - Ip = option.IpAddress.ToUtf8ByteString(), + Address = option.Address, Blacklist = option.BlackListFlag }; } diff --git a/src/Catalyst.Cli/Commands/PeerCountCommand.cs b/src/Catalyst.Cli/Commands/PeerCountCommand.cs index 7d20d5dc0e..423765da38 100644 --- a/src/Catalyst.Cli/Commands/PeerCountCommand.cs +++ b/src/Catalyst.Cli/Commands/PeerCountCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,9 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; using Serilog; diff --git a/src/Catalyst.Cli/Commands/PeerListCommand.cs b/src/Catalyst.Cli/Commands/PeerListCommand.cs index dd9bc9cebb..5db836977c 100644 --- a/src/Catalyst.Cli/Commands/PeerListCommand.cs +++ b/src/Catalyst.Cli/Commands/PeerListCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,9 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; using Serilog; diff --git a/src/Catalyst.Cli/Commands/PeerRemoveCommand.cs b/src/Catalyst.Cli/Commands/PeerRemoveCommand.cs index e1b8002c75..3a2c8706f8 100644 --- a/src/Catalyst.Cli/Commands/PeerRemoveCommand.cs +++ b/src/Catalyst.Cli/Commands/PeerRemoveCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,12 +21,10 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; -using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; -using Google.Protobuf; using Serilog; namespace Catalyst.Cli.Commands @@ -40,10 +38,7 @@ protected override RemovePeerRequest GetMessage(RemovePeerOptions option) { return new RemovePeerRequest { - PeerIp = option.Ip.ToUtf8ByteString(), - PublicKey = string.IsNullOrEmpty(option.PublicKey) - ? ByteString.Empty - : option.PublicKey.ToUtf8ByteString() + Address = option.Address }; } } diff --git a/src/Catalyst.Cli/Commands/PeerReputationCommand.cs b/src/Catalyst.Cli/Commands/PeerReputationCommand.cs index 645cf41742..4a703e4281 100644 --- a/src/Catalyst.Cli/Commands/PeerReputationCommand.cs +++ b/src/Catalyst.Cli/Commands/PeerReputationCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using Catalyst.Abstractions.Cli.Commands; using Catalyst.Cli.CommandTypes; using Catalyst.Cli.Options; using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands; using Catalyst.Protocol.Rpc.Node; using Serilog; @@ -40,8 +40,7 @@ protected override GetPeerReputationRequest GetMessage(PeerReputationOptions opt { return new GetPeerReputationRequest { - PublicKey = option.PublicKey.ToUtf8ByteString(), - Ip = option.IpAddress.ToUtf8ByteString() + Address = option.Address }; } } diff --git a/src/Catalyst.Cli/Config/nodes.json b/src/Catalyst.Cli/Config/nodes.json index 8f348b1f68..d206a9551a 100644 --- a/src/Catalyst.Cli/Config/nodes.json +++ b/src/Catalyst.Cli/Config/nodes.json @@ -1,22 +1,18 @@ { "CatalystCliRpcNodes": { "nodes": [ - { - "nodeId": "node1", - "host": "127.0.0.1", - "Port": 42066, - "PfxFileName": "mycert.pfx", - "SslCertPassword": "test", - "PublicKey": "hv6vvbt2u567syz5labuqnfabsc3zobfwekl4cy3c574n6vkj7sq" - }, - { - "nodeId": "node2", - "host": "127.0.0.2", - "Port": 42066, - "PfxFileName": "mycert.pfx", - "SslCertPassword": "test", - "PublicKey": "hv6vvbt2u567syz5labuqnfabsc3zobfwekl4cy3c574n6vkj7sq" - } + { + "nodeId": "node1", + "Address": "/ip4/127.0.0.1/tcp/42066/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4", + "PfxFileName": "mycert.pfx", + "SslCertPassword": "test" + }, + { + "nodeId": "node2", + "Address": "/ip4/127.0.0.2/tcp/42066/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4", + "PfxFileName": "mycert.pfx", + "SslCertPassword": "test" + } ] } } diff --git a/src/Catalyst.Cli/Config/shell.config.json b/src/Catalyst.Cli/Config/shell.config.json index c63c06c3d3..c818aca3a2 100644 --- a/src/Catalyst.Cli/Config/shell.config.json +++ b/src/Catalyst.Cli/Config/shell.config.json @@ -1,7 +1,5 @@ { - "CatalystCliConfig": { - "BindAddress": "127.0.0.1", - "Port": 5266, - "PublicKey": "hv6vvbt2u567syz5labuqnfabsc3zobfwekl4cy3c574n6vkj7sq" - } + "CatalystCliConfig": { + "Address": "/ip4/127.0.0.1/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4" + } } diff --git a/src/Catalyst.Cli/Options/AddFileOptions.cs b/src/Catalyst.Cli/Options/AddFileOptions.cs index a9a569098c..d9f418197a 100644 --- a/src/Catalyst.Cli/Options/AddFileOptions.cs +++ b/src/Catalyst.Cli/Options/AddFileOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/ChangeDataFolderOptions.cs b/src/Catalyst.Cli/Options/ChangeDataFolderOptions.cs index 5bce13d76b..3b21f4c835 100644 --- a/src/Catalyst.Cli/Options/ChangeDataFolderOptions.cs +++ b/src/Catalyst.Cli/Options/ChangeDataFolderOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/ConnectOptions.cs b/src/Catalyst.Cli/Options/ConnectOptions.cs index 6bd0c6e2f1..a4cf468bbe 100644 --- a/src/Catalyst.Cli/Options/ConnectOptions.cs +++ b/src/Catalyst.Cli/Options/ConnectOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/DisconnectOptions.cs b/src/Catalyst.Cli/Options/DisconnectOptions.cs index f8e3728a45..866a64d252 100644 --- a/src/Catalyst.Cli/Options/DisconnectOptions.cs +++ b/src/Catalyst.Cli/Options/DisconnectOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/GetDeltaOptions.cs b/src/Catalyst.Cli/Options/GetDeltaOptions.cs index c2689e4ede..769ae1c42f 100644 --- a/src/Catalyst.Cli/Options/GetDeltaOptions.cs +++ b/src/Catalyst.Cli/Options/GetDeltaOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/GetFileOptions.cs b/src/Catalyst.Cli/Options/GetFileOptions.cs index 66e950ab28..33b11d5cf0 100644 --- a/src/Catalyst.Cli/Options/GetFileOptions.cs +++ b/src/Catalyst.Cli/Options/GetFileOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/GetInfoOptions.cs b/src/Catalyst.Cli/Options/GetInfoOptions.cs index c10e55725b..a636b27eb3 100644 --- a/src/Catalyst.Cli/Options/GetInfoOptions.cs +++ b/src/Catalyst.Cli/Options/GetInfoOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/GetMempoolOptions.cs b/src/Catalyst.Cli/Options/GetMempoolOptions.cs index bd5d755b09..d3a50d3d04 100644 --- a/src/Catalyst.Cli/Options/GetMempoolOptions.cs +++ b/src/Catalyst.Cli/Options/GetMempoolOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/GetPeerInfoOptions.cs b/src/Catalyst.Cli/Options/GetPeerInfoOptions.cs index 5e3e3185d0..fa1c63ec48 100644 --- a/src/Catalyst.Cli/Options/GetPeerInfoOptions.cs +++ b/src/Catalyst.Cli/Options/GetPeerInfoOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,11 +30,7 @@ namespace Catalyst.Cli.Options public sealed class GetPeerInfoOptions : OptionsBase, IGetPeerInfoOptions { /// - [Option('i', "ip", HelpText = "IP address of the peer whose info is of interest.")] - public string IpAddress { get; set; } - - /// - [Option('p', "publickey", HelpText = "Public key of the peer whose info is of interest.")] - public string PublicKey { get; set; } + [Option('a', "address", HelpText = "MultiAddress of the peer whose info is of interest.")] + public string Address { get; set; } } } diff --git a/src/Catalyst.Cli/Options/GetVersionOptions.cs b/src/Catalyst.Cli/Options/GetVersionOptions.cs index a23512c20d..76922e454e 100644 --- a/src/Catalyst.Cli/Options/GetVersionOptions.cs +++ b/src/Catalyst.Cli/Options/GetVersionOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/OptionsBase.cs b/src/Catalyst.Cli/Options/OptionsBase.cs index e47f3869ac..470080fe58 100644 --- a/src/Catalyst.Cli/Options/OptionsBase.cs +++ b/src/Catalyst.Cli/Options/OptionsBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/PeerBlackListingOptions.cs b/src/Catalyst.Cli/Options/PeerBlackListingOptions.cs index 1a7658aea4..bf19bc82c3 100644 --- a/src/Catalyst.Cli/Options/PeerBlackListingOptions.cs +++ b/src/Catalyst.Cli/Options/PeerBlackListingOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -38,13 +38,8 @@ public sealed class PeerBlackListingOptions : OptionsBase, IPeerBlackListingOpti [Option('b', "blacklistflag", HelpText = "Blacklist flag for peer.")] public bool BlackListFlag { get; set; } - /// - [Option('i', "ip", HelpText = "IP address of the peer to blacklist.")] - public string IpAddress { get; set; } - - /// - [Option('p', "publickey", HelpText = "Public key of the peer to blacklist.")] - public string PublicKey { get; set; } + [Option('a', "address", HelpText = "MultiAddress of the peer whose info is of interest.")] + public string Address { get; set; } /// /// Gets the examples. diff --git a/src/Catalyst.Cli/Options/PeerCountOptions.cs b/src/Catalyst.Cli/Options/PeerCountOptions.cs index 8c5cbfe63b..517a8d52a9 100644 --- a/src/Catalyst.Cli/Options/PeerCountOptions.cs +++ b/src/Catalyst.Cli/Options/PeerCountOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/PeerListOptions.cs b/src/Catalyst.Cli/Options/PeerListOptions.cs index acdbc8e850..4214e4702f 100644 --- a/src/Catalyst.Cli/Options/PeerListOptions.cs +++ b/src/Catalyst.Cli/Options/PeerListOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/PeerReputationOptions.cs b/src/Catalyst.Cli/Options/PeerReputationOptions.cs index bfeaf71cfc..f7550ccd32 100644 --- a/src/Catalyst.Cli/Options/PeerReputationOptions.cs +++ b/src/Catalyst.Cli/Options/PeerReputationOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -35,12 +35,8 @@ namespace Catalyst.Cli.Options public sealed class PeerReputationOptions : OptionsBase, IPeerReputationOptions { /// - [Option('i', "ip", HelpText = "IP address of the peer whose reputation is of interest.")] - public string IpAddress { get; set; } - - /// - [Option('p', "publickey", HelpText = "Public key of the peer whose reputation is of interest.")] - public string PublicKey { get; set; } + [Option('a', "address", HelpText = "MultiAddress of the peer whose info is of interest.")] + public string Address { get; set; } /// /// Gets the examples. diff --git a/src/Catalyst.Cli/Options/RemovePeerOptions.cs b/src/Catalyst.Cli/Options/RemovePeerOptions.cs index 4bb90de770..229cd911e4 100644 --- a/src/Catalyst.Cli/Options/RemovePeerOptions.cs +++ b/src/Catalyst.Cli/Options/RemovePeerOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,6 +25,7 @@ using Catalyst.Abstractions.Cli.Options; using CommandLine; using CommandLine.Text; +using MultiFormats; namespace Catalyst.Cli.Options { @@ -34,13 +35,8 @@ namespace Catalyst.Cli.Options [Verb("removepeer", HelpText = "removes a peer")] public sealed class RemovePeerOptions : OptionsBase, IRemovePeerOptions { - /// - [Option('p', "publickey", HelpText = "Public key of the peer to remove.", Required = false)] - public string PublicKey { get; set; } - - /// - [Option('i', "ip", HelpText = "IP address of the peer to remove.", Required = true)] - public string Ip { get; set; } + [Option('a', "address", HelpText = "MultiAddress of the peer whose info is of interest.")] + public string Address { get; set; } /// /// Gets the examples. @@ -53,7 +49,7 @@ public sealed class RemovePeerOptions : OptionsBase, IRemovePeerOptions new List { new Example("Removes a peer from the specified node.", - new RemovePeerOptions {Ip = "127.0.0.1", Node = "node1", PublicKey = "302a300506032b657003"}) + new MultiAddress("/ip4/192.168.0.181/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4")) }; } } diff --git a/src/Catalyst.Cli/Options/SignOptions.cs b/src/Catalyst.Cli/Options/SignOptions.cs index f95ad2b14a..3e8752122e 100644 --- a/src/Catalyst.Cli/Options/SignOptions.cs +++ b/src/Catalyst.Cli/Options/SignOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Options/VerifyOptions.cs b/src/Catalyst.Cli/Options/VerifyOptions.cs index f7df5ce618..0bded50410 100644 --- a/src/Catalyst.Cli/Options/VerifyOptions.cs +++ b/src/Catalyst.Cli/Options/VerifyOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Cli/Program.cs b/src/Catalyst.Cli/Program.cs index 4d905372f8..e4fa046408 100644 --- a/src/Catalyst.Cli/Program.cs +++ b/src/Catalyst.Cli/Program.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -54,17 +54,16 @@ static Program() /// /// Main cli loop /// - public static int Main(string[] args) + public static async Task Main(string[] args) { // Parse the arguments. - Parser.Default - .ParseArguments(args) - .WithParsed(async o => await RunAsync(o).ConfigureAwait(false)); - - return Environment.ExitCode; + var result = await Parser.Default.ParseArguments(args) + .MapResult(async options => await RunAsync(options).ConfigureAwait(false), + response => Task.FromResult(1)).ConfigureAwait(false); + return Environment.ExitCode = result; } - private static async Task RunAsync(Options options) + private static async Task RunAsync(Options options) { Kernel.Logger.Information("Catalyst.Cli started with process id {0}", Process.GetCurrentProcess().Id.ToString()); @@ -76,20 +75,21 @@ await Kernel.WithDataDirectory() .WithConfigCopier(new CliConfigCopier()) .WithConfigurationFile(CliConstants.ShellNodesConfigFile) .WithConfigurationFile(CliConstants.ShellConfigFile) - .WithNetworksConfigFile(NetworkType.Devnet, options.OverrideNetworkFile) + .WithNetworkType(NetworkType.Testnet) + .WithNetworksConfigFile(options.OverrideNetworkFile) .BuildKernel() - .StartCustomAsync(StartCli); + .StartCustomAsync(StartCliAsync); - Environment.ExitCode = 0; + return 0; } catch (Exception e) { Kernel.Logger.Fatal(e, "Catalyst.Cli stopped unexpectedly"); - Environment.ExitCode = 1; + return 1; } } - private static async Task StartCli(Kernel kernel) + private static void StartCli(Kernel kernel) { const int bufferSize = 1024 * 67 + 128; @@ -110,5 +110,10 @@ private static async Task StartCli(Kernel kernel) kernel.Instance.Resolve() .RunConsole(kernel.CancellationTokenProvider.CancellationTokenSource.Token); } + + private static async Task StartCliAsync(Kernel kernel) + { + await Task.Run(() => StartCli(kernel)).ConfigureAwait(false); + } } } diff --git a/src/Catalyst.Core.Lib.Tests/Catalyst.Core.Lib.Tests.csproj b/src/Catalyst.Core.Lib.Tests/Catalyst.Core.Lib.Tests.csproj index 59697b1d42..4162f0c049 100644 --- a/src/Catalyst.Core.Lib.Tests/Catalyst.Core.Lib.Tests.csproj +++ b/src/Catalyst.Core.Lib.Tests/Catalyst.Core.Lib.Tests.csproj @@ -1,32 +1,39 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Lib.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Lib.Tests.snk true + + 1701;1702;VSTHRD200;CS8002 + - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - - - - - - - - - - - - - - + + + + + + + + + + + + + + diff --git a/src/Catalyst.Core.Lib.Tests/Catalyst.Core.Lib.Tests.csproj.DotSettings b/src/Catalyst.Core.Lib.Tests/Catalyst.Core.Lib.Tests.csproj.DotSettings new file mode 100644 index 0000000000..b233cd097a --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/Catalyst.Core.Lib.Tests.csproj.DotSettings @@ -0,0 +1,2 @@ + + True \ No newline at end of file diff --git a/src/Catalyst.Core.Lib.Tests/ConfigApiTest.cs b/src/Catalyst.Core.Lib.Tests/ConfigApiTest.cs new file mode 100644 index 0000000000..cb237f6a97 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/ConfigApiTest.cs @@ -0,0 +1,106 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.Config; +using Catalyst.TestUtils; +using Makaretu.Dns; +using Newtonsoft.Json.Linq; +using NSubstitute; +using NUnit.Framework; + + +namespace Catalyst.Core.Lib.Tests +{ + public sealed class ConfigApiTest : FileSystemBasedTest + { + private IConfigApi _configApi; + + private const string ApiAddress = "/ip4/127.0.0.1/tcp/"; + private const string GatewayAddress = "/ip4/127.0.0.1/tcp/"; + + [SetUp] + public void Init() + { + this.Setup(TestContext.CurrentContext); + DfsOptions dfsOptions = new(Substitute.For(), Substitute.For(), new RepositoryOptions(FileSystem, Constants.DfsDataSubDir), Substitute.For(), Substitute.For(), Substitute.For()); + _configApi = new ConfigApi(dfsOptions); + } + + [Test] + public async Task Get_Entire_Config() + { + var config = await _configApi.GetAsync(); + + Assert.That(config["Addresses"]["API"].Value(), Does.StartWith(ApiAddress)); + } + + [Test] + public async Task Get_Scalar_Key_Value() + { + var api = await _configApi.GetAsync("Addresses.API"); + Assert.That(api.Value().StartsWith(ApiAddress)); + } + + [Test] + public async Task Get_Object_Key_Value() + { + var addresses = await _configApi.GetAsync("Addresses"); + Assert.That(addresses["API"].Value().StartsWith(ApiAddress)); + Assert.That(addresses["Gateway"].Value().StartsWith(GatewayAddress)); + } + + [Test] + public void Keys_are_Case_Sensitive() + { + var api = _configApi.GetAsync("Addresses.API").Result; + Assert.That(api.Value().StartsWith(ApiAddress)); + + ExceptionAssert.Throws(() => + { + _configApi.GetAsync("Addresses.api").GetAwaiter().GetResult(); + }); + } + + [Test] + public async Task Set_String_Value() + { + const string key = "foo"; + const string value = "foobar"; + await _configApi.SetAsync(key, value); + Assert.AreEqual(value, (await _configApi.GetAsync(key)).ToString()); + } + + [Test] + public async Task Set_JSON_Value() + { + const string key = "API.HTTPHeaders.Access-Control-Allow-Origin"; + var value = JToken.Parse("['http://example.io']"); + await _configApi.SetAsync(key, value); + Assert.AreEqual("http://example.io", _configApi.GetAsync(key).Result[0].ToString()); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Cryptography/CertificateStoreTests.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/Cryptography/CertificateStoreTests.cs index 150b4a97e2..23cb9e3a5b 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Cryptography/CertificateStoreTests.cs +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/Cryptography/CertificateStoreTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,15 +29,18 @@ using Catalyst.TestUtils; using FluentAssertions; using NSubstitute; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.IntegrationTests.Cryptography { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class CertificateStoreTests : FileSystemBasedTest { - public CertificateStoreTests(ITestOutputHelper output) : base(output) + [SetUp] + public void Init() { + Setup(TestContext.CurrentContext); _passwordManager = Substitute.For(); } @@ -46,7 +49,7 @@ public CertificateStoreTests(ITestOutputHelper output) : base(output) private X509Certificate2 _createdCertificate; private X509Certificate2 _retrievedCertificate; - private readonly IPasswordManager _passwordManager; + private IPasswordManager _passwordManager; private void Create_certificate_store() { @@ -90,8 +93,7 @@ protected override void Dispose(bool disposing) _retrievedCertificate?.Dispose(); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void CertificateStore_CanReadAndWriteCertFiles_WithPassword() { if (Environment.OSVersion.Platform == PlatformID.Unix) diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/FileSystem/FileStoreTest.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/FileSystem/FileStoreTest.cs new file mode 100644 index 0000000000..279ad75a9b --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/FileSystem/FileStoreTest.cs @@ -0,0 +1,202 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Core.Lib.FileSystem; +using Catalyst.TestUtils; +using NUnit.Framework; + + +namespace Catalyst.Core.Lib.Tests.IntegrationTests.FileSystem +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class FileStoreTest : FileSystemBasedTest + { + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } + + private sealed class Entity + { + public int Number; + public string Value; + } + + private readonly Entity _a = new Entity {Number = 1, Value = "a"}; + private readonly Entity _b = new Entity {Number = 2, Value = "b"}; + + private FileStore Store + { + get + { + var folder = Path.Combine(FileSystem.GetCatalystDataDir().FullName, "test-filestore"); + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } + + return new FileStore + { + Folder = folder, + NameToKey = name => name.ToString(), + KeyToName = int.Parse + }; + } + } + + [Test] + public async Task PutAndGet() + { + var store = Store; + + await store.PutAsync(_a.Number, _a); + await store.PutAsync(_b.Number, _b); + + var a1 = await store.GetAsync(_a.Number); + Assert.AreEqual(_a.Number, a1.Number); + Assert.AreEqual(_a.Value, a1.Value); + + var b1 = await store.GetAsync(_b.Number); + Assert.AreEqual(_b.Number, b1.Number); + Assert.AreEqual(_b.Value, b1.Value); + } + + [Test] + public async Task TryGet() + { + var store = Store; + await store.PutAsync(3, _a); + var a1 = await store.GetAsync(3); + Assert.AreEqual(_a.Number, a1.Number); + Assert.AreEqual(_a.Value, a1.Value); + + var a3 = await store.TryGetAsync(42); + Assert.Null(a3); + } + + [Test] + public void Get_Unknown() + { + ExceptionAssert.Throws(() => + { + var _ = Store.GetAsync(42).Result; + }); + } + + [Test] + public async Task Remove() + { + var store = Store; + await store.PutAsync(4, _a); + Assert.NotNull(await store.TryGetAsync(4)); + + await store.RemoveAsync(4); + Assert.Null(await store.TryGetAsync(4)); + } + + [Test] + public async Task Remove_Unknown() + { + var store = Store; + await store.RemoveAsync(5); + } + + [Test] + public async Task Length() + { + var store = Store; + await store.PutAsync(6, _a); + var length = await store.LengthAsync(6); + Assert.True(length.HasValue); + Assert.AreNotEqual(0, length.Value); + } + + [Test] + public async Task Length_Unknown() + { + var store = Store; + var length = await store.LengthAsync(7); + Assert.False(length.HasValue); + } + + [Test] + public async Task Values() + { + var store = Store; + await store.PutAsync(8, new Entity {Value = "v0"}); + await store.PutAsync(9, new Entity {Value = "v1"}); + await store.PutAsync(10, new Entity {Value = "v0"}); + var values = Store.Values.Where(e => e.Value == "v0").ToArray(); + Assert.AreEqual(2, values.Length); + } + + [Test] + public async Task Names() + { + var store = Store; + await store.PutAsync(11, _a); + await store.PutAsync(12, _a); + await store.PutAsync(13, _a); + var names = Store.Names.Where(n => n == 11 || n == 13).ToArray(); + Assert.AreEqual(2, names.Length); + } + + [Test] + public async Task Atomic() + { + var store = Store; + const int nTasks = 100; + var tasks = Enumerable + .Range(1, nTasks) + .Select(i => Task.Run(() => AtomicTask(store))) + .ToArray(); + await Task.WhenAll(tasks); + } + + private async Task AtomicTask(FileStore store) + { + await store.PutAsync(1, _a); + await store.TryGetAsync(1); + await store.RemoveAsync(1); + } + + [Test] + public void PutWithException() + { + Task BadSerialize(Stream stream, int name, Entity value, CancellationToken cancel) => throw new Exception("no serializer"); + var store = Store; + store.Serialize = BadSerialize; + + ExceptionAssert.Throws(() => store.PutAsync(_a.Number, _a).Wait()); + Assert.False(store.ExistsAsync(_a.Number).Result); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Network/DnsIntergrationTests.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/Network/DnsIntergrationTests.cs index eb713d1b69..628fac1d82 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Network/DnsIntergrationTests.cs +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/Network/DnsIntergrationTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,22 +28,23 @@ using DnsClient; using DnsClient.Protocol; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.IntegrationTests.Network { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class DnsIntegrationTests { - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public async Task GetTxtRecords_should_return_seeds() { var trueClient = new LookupClient(new IPEndPoint(IPAddress.Parse("9.9.9.9"), 53)); var dns = new Lib.Network.DnsClient(trueClient); var dnsQueryResponse = - await dns.GetTxtRecordsAsync("seed1.network.atlascity.io"); + await dns.GetTxtRecordsAsync("seed1.network.catalystnet.org"); var answerSection = (TxtRecord) dnsQueryResponse.Answers.FirstOrDefault(); - var seedIp = answerSection.EscapedText.FirstOrDefault(); + var seedIp = answerSection?.EscapedText.FirstOrDefault(); seedIp.Should().Be("0x41437c30317c39322e3230372e3137382e3139387c34323036397c3031323334353637383930313233343536373839"); } } diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Network/IpIntergrationTests.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/Network/IpIntergrationTests.cs index 6e383b3ecf..94041bc726 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Network/IpIntergrationTests.cs +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/Network/IpIntergrationTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,36 +30,38 @@ using Catalyst.Core.Lib.Network; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.IntegrationTests.Network { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class IpIntegrationTests { - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public async Task GetPublicIp_should_not_wait_for_longest_response() - { - var delayedObservable = Ip.DefaultIpEchoUrls - .Select((o, i) => - { - return i != 2 - ? Observable.Timer(TimeSpan.FromSeconds(5)).Select(_ => o) - : Observable.Return(o); - }).Merge(); + //todo issue: https://github.com/catalyst-network/Catalyst/issues/1234 + //[Test] + //[Property(Traits.TestType, Traits.IntegrationTest)] + //public async Task GetPublicIp_should_not_wait_for_longest_response() + //{ + // var delayedObservable = Ip.DefaultIpEchoUrls + // .Select((o, i) => + // { + // return i != 2 + // ? Observable.Timer(TimeSpan.FromSeconds(5)).Select(_ => o) + // : Observable.Return(o); + // }).Merge(); - var stopWatch = new Stopwatch(); + // var stopWatch = new Stopwatch(); - stopWatch.Start(); - var myIp = await Ip.GetPublicIpAsync(delayedObservable); - stopWatch.Stop(); + // stopWatch.Start(); + // var myIp = await Ip.GetPublicIpAsync(delayedObservable); + // stopWatch.Stop(); - myIp.Should().NotBe(default(IPAddress)); - stopWatch.Elapsed.Should().BeLessThan(TimeSpan.FromSeconds(3)); - } + // myIp.Should().NotBe(default(IPAddress)); + // stopWatch.Elapsed.Should().BeLessThan(TimeSpan.FromSeconds(3)); + //} - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public async Task GetPublicIp_should_tolerate_echo_failure() { var echoUrlWithFailure = new[] {"https://this.will.fail.for.sure"} @@ -68,8 +70,7 @@ public async Task GetPublicIp_should_tolerate_echo_failure() myIp.Should().NotBe(default(IPAddress)); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public async Task GetPublicIp_should_usually_return_a_valid_ip() { var myIp = await Ip.GetPublicIpAsync(); diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/IO/Transport/Channels/PeerClientChannelFactoryTests.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/IO/Transport/Channels/PeerClientChannelFactoryTests.cs index 38dd843dac..58b3952a22 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/IO/Transport/Channels/PeerClientChannelFactoryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/IO/Transport/Channels/PeerClientChannelFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,47 +24,51 @@ using System.Linq; using System.Threading.Tasks; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Protocol.Wire; using Catalyst.Protocol.IPPN; using Catalyst.Protocol.Network; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using DotNetty.Transport.Channels.Embedded; using DotNetty.Transport.Channels.Sockets; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; namespace Catalyst.Core.Lib.Tests.IntegrationTests.P2P.IO.Transport.Channels { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class PeerClientChannelFactoryTests { - private readonly TestScheduler _testScheduler; - private readonly UnitTests.P2P.IO.Transport.Channels.PeerClientChannelFactoryTests.TestPeerClientChannelFactory _clientFactory; - private readonly EmbeddedChannel _serverChannel; - private readonly EmbeddedChannel _clientChannel; - private readonly IPeerMessageCorrelationManager _clientCorrelationManager; - private readonly IKeySigner _clientKeySigner; - private readonly IPeerIdValidator _peerIdValidator; - private readonly IKeySigner _serverKeySigner; - private readonly IPeerMessageCorrelationManager _serverCorrelationManager; - private readonly ISignature _signature; - - public PeerClientChannelFactoryTests() + private TestScheduler _testScheduler; + private UnitTests.P2P.IO.Transport.Channels.PeerClientChannelFactoryTests.TestPeerClientChannelFactory _clientFactory; + private EmbeddedChannel _serverChannel; + private EmbeddedChannel _clientChannel; + private IPeerMessageCorrelationManager _clientCorrelationManager; + private FakeKeySigner _clientKeySigner; + private IPeerIdValidator _peerIdValidator; + private FakeKeySigner _serverKeySigner; + private IPeerMessageCorrelationManager _serverCorrelationManager; + private ISignature _signature; + + [SetUp] + public void Init() { _testScheduler = new TestScheduler(); _serverCorrelationManager = Substitute.For(); - _serverKeySigner = Substitute.For(); + _serverKeySigner = Substitute.For(); _serverKeySigner.CryptoContext.SignatureLength.Returns(64); var broadcastManager = Substitute.For(); @@ -72,7 +76,7 @@ public PeerClientChannelFactoryTests() var peerSettings = Substitute.For(); peerSettings.NetworkType.Returns(NetworkType.Devnet); - + var serverFactory = new UnitTests.P2P.IO.Transport.Channels.PeerServerChannelFactoryTests.TestPeerServerChannelFactory( _serverCorrelationManager, broadcastManager, @@ -84,11 +88,11 @@ public PeerClientChannelFactoryTests() _signature = Substitute.For(); _clientCorrelationManager = Substitute.For(); - _clientKeySigner = Substitute.For(); + _clientKeySigner = Substitute.For(); _clientKeySigner.CryptoContext.SignatureLength.Returns(64); _clientFactory = new UnitTests.P2P.IO.Transport.Channels.PeerClientChannelFactoryTests.TestPeerClientChannelFactory( - _clientKeySigner, + _clientKeySigner, _clientCorrelationManager, _peerIdValidator, peerSettings, @@ -96,48 +100,47 @@ public PeerClientChannelFactoryTests() _serverChannel = new EmbeddedChannel("server".ToChannelId(), true, serverFactory.InheritedHandlers.ToArray()); - + _clientChannel = new EmbeddedChannel("client".ToChannelId(), true, _clientFactory.InheritedHandlers.ToArray()); } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public async Task + + [Test] +public async Task PeerClientChannelFactory_Pipeline_Should_Produce_Request_Object_PeerClientChannelFactory_Can_Process() { - var recipient = PeerIdHelper.GetPeerId("recipient"); - var sender = PeerIdHelper.GetPeerId("sender"); - _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); + var recipient = MultiAddressHelper.GetAddress("recipient"); + var sender = MultiAddressHelper.GetAddress("sender"); + _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); _serverKeySigner.Sign(Arg.Any(), default).ReturnsForAnyArgs(_signature); - + var correlationId = CorrelationId.GenerateCorrelationId(); var protocolMessage = new PingRequest().ToProtocolMessage(sender, correlationId); var dto = new MessageDto(protocolMessage, recipient); _clientCorrelationManager.TryMatchResponse(Arg.Any()).Returns(true); - + _serverChannel.WriteOutbound(dto); var sentBytes = _serverChannel.ReadOutbound(); _serverCorrelationManager.ReceivedWithAnyArgs(1) .AddPendingRequest(Arg.Any>()); - + _serverKeySigner.ReceivedWithAnyArgs(1).Sign(Arg.Any(), default); - + _clientKeySigner.Verify( Arg.Any(), - Arg.Any(), + Arg.Any(), default ) .ReturnsForAnyArgs(true); - - var observer = new ProtocolMessageObserver(0, Substitute.For()); + + var observer = new ProtocolMessageObserver(0, Substitute.For()); var messageStream = _clientFactory.InheritedHandlers.OfType().Single().MessageStream; - + using (messageStream.Subscribe(observer)) { _clientChannel.WriteInbound(sentBytes); @@ -149,9 +152,9 @@ public async Task _testScheduler.Start(); observer.Received.Count.Should().Be(1); - observer.Received.Single().Payload.CorrelationId.ToCorrelationId().Id.Should().Be(correlationId.Id); + observer.Received.Single().CorrelationId.ToCorrelationId().Id.Should().Be(correlationId.Id); } - + await _serverChannel.DisconnectAsync(); await _clientChannel.DisconnectAsync(); } diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/IO/Transport/Channels/PeerServerChannelFactoryTests.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/IO/Transport/Channels/PeerServerChannelFactoryTests.cs index 174179fd76..7f7ffc8680 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/IO/Transport/Channels/PeerServerChannelFactoryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/IO/Transport/Channels/PeerServerChannelFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,29 +24,31 @@ using System.Linq; using System.Threading.Tasks; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Protocol.Wire; using Catalyst.Protocol.IPPN; using Catalyst.Protocol.Network; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using DotNetty.Transport.Channels.Embedded; using DotNetty.Transport.Channels.Sockets; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; namespace Catalyst.Core.Lib.Tests.IntegrationTests.P2P.IO.Transport.Channels { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class PeerServerChannelFactoryTests { private readonly TestScheduler _testScheduler; @@ -57,16 +59,16 @@ private readonly UnitTests.P2P.IO.Transport.Channels.PeerClientChannelFactoryTes private readonly EmbeddedChannel _serverChannel; private readonly EmbeddedChannel _clientChannel; private readonly IPeerMessageCorrelationManager _clientCorrelationManager; - private readonly IKeySigner _clientKeySigner; + private readonly FakeKeySigner _clientKeySigner; private readonly IPeerIdValidator _peerIdValidator; - private readonly IKeySigner _serverKeySigner; + private readonly FakeKeySigner _serverKeySigner; private readonly IPeerMessageCorrelationManager _serverCorrelationManager; public PeerServerChannelFactoryTests() { _testScheduler = new TestScheduler(); _serverCorrelationManager = Substitute.For(); - _serverKeySigner = Substitute.For(); + _serverKeySigner = Substitute.For(); var broadcastManager = Substitute.For(); _peerIdValidator = Substitute.For(); @@ -84,7 +86,7 @@ public PeerServerChannelFactoryTests() _testScheduler); _clientCorrelationManager = Substitute.For(); - _clientKeySigner = Substitute.For(); + _clientKeySigner = Substitute.For(); _clientFactory = new UnitTests.P2P.IO.Transport.Channels.PeerClientChannelFactoryTests.TestPeerClientChannelFactory( @@ -101,16 +103,15 @@ public PeerServerChannelFactoryTests() new EmbeddedChannel("client".ToChannelId(), true, _clientFactory.InheritedHandlers.ToArray()); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public async Task PeerServerChannelFactory_Pipeline_Should_Produce_Response_Object_PeerClientChannelFactory_Can_Process() { - var recipient = PeerIdHelper.GetPeerId("recipient"); - var sender = PeerIdHelper.GetPeerId("sender"); + var recipient = MultiAddressHelper.GetAddress("recipient"); + var sender = MultiAddressHelper.GetAddress("sender"); var signature = Substitute.For(); - _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); + _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); _serverKeySigner.CryptoContext.SignatureLength.Returns(64); _serverKeySigner.Sign(Arg.Any(), default).ReturnsForAnyArgs(signature); @@ -138,7 +139,7 @@ public async Task default) .ReturnsForAnyArgs(true); - var observer = new ProtocolMessageObserver(0, Substitute.For()); + var observer = new ProtocolMessageObserver(0, Substitute.For()); var messageStream = _clientFactory.InheritedHandlers.OfType().Single() .MessageStream; @@ -154,7 +155,7 @@ public async Task _testScheduler.Start(); observer.Received.Count.Should().Be(1); - observer.Received.Single().Payload.CorrelationId.ToCorrelationId().Id.Should().Be(correlationId.Id); + observer.Received.Single().CorrelationId.ToCorrelationId().Id.Should().Be(correlationId.Id); } await _serverChannel.DisconnectAsync(); diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerRepositoryTests.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerRepositoryTests.cs index ab0985131f..149665966d 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerRepositoryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerRepositoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,30 +25,32 @@ using System.Collections.Generic; using Autofac; using Catalyst.TestUtils; -using Xunit; -using Xunit.Abstractions; -using Catalyst.Core.Lib.DAO; -using Catalyst.Core.Lib.Repository; -using Catalyst.Protocol.Peer; +using Catalyst.Core.Lib.DAO.Peer; +using Catalyst.Core.Lib.Service; using SharpRepository.Repository; using FluentAssertions; using Microsoft.EntityFrameworkCore; using Catalyst.TestUtils.Repository; +using NUnit.Framework; +using MultiFormats; namespace Catalyst.Core.Lib.Tests.IntegrationTests.P2P { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class PeerRepositoryTests : FileSystemBasedTest { - private readonly TestMapperProvider _mapperProvider; + private TestMapperProvider _mapperProvider; - public static IEnumerable ModulesList => + public static IEnumerable ModulesList => new List { new object[] {new InMemoryTestModule()}, new object[] {new MongoDbTestModule()} }; - public PeerRepositoryTests(ITestOutputHelper output) : base(output) + [SetUp] + public void Init() { _mapperProvider = new TestMapperProvider(); } @@ -60,8 +62,7 @@ private void PeerRepo_Can_Save_And_Retrieve() var peerRepo = PopulatePeerRepo(scope, out var peerDao); peerRepo.Get(peerDao.Id).Id.Should().Be(peerDao.Id); - peerRepo.Get(peerDao.Id).PeerIdentifier.PublicKey.Should().Be(peerDao.PeerIdentifier.PublicKey); - peerRepo.Get(peerDao.Id).PeerIdentifier.Ip.Should().Be(peerDao.PeerIdentifier.Ip); + new MultiAddress(peerRepo.Get(peerDao.Id).Address).Should().Be(peerDao.Address); } } @@ -84,7 +85,7 @@ private void PeerRepo_Can_Update_And_Retrieve() } var dateComparer = retrievedPeerModified.Modified.Value.Date.ToString("MM/dd/yyyy"); - + // ReSharper disable once SuspiciousTypeConversion.Global // ReSharper disable once ReturnValueOfPureMethodIsNotUsed dateComparer.Should()?.Equals(now.ToString("MM/dd/yyyy")); @@ -99,39 +100,38 @@ private IRepository PopulatePeerRepo(ILifetimeScope scope, out { Id = Guid.NewGuid().ToString() }; - - var peerId = PeerIdHelper.GetPeerId(new Random().Next().ToString()); - peerDao.PeerIdentifier = peerId.ToDao(_mapperProvider); - peerDao.PeerIdentifier.Id = Guid.NewGuid().ToString(); + + var address = MultiAddressHelper.GetAddress(new Random().Next().ToString()); + peerDao.Address = address.ToString(); peerRepo.Add(peerDao); peerDaoOutput = peerDao; return peerRepo; } - - [Theory(Skip = "Setup to run in pipeline only")] - [Trait(Traits.TestType, Traits.E2EMongoDb)] - [MemberData(nameof(ModulesList))] - public void PeerRepo_All_Dbs_Can_Update_And_Retrieve(Module dbModule) + + [Ignore("Setup to run in pipeline only")] + [Category(Traits.E2EMongoDb)] + [TestCase(nameof(ModulesList))] + public void PeerRepo_All_Dbs_Can_Update_And_Retrieve(Autofac.Module dbModule) { RegisterModules(dbModule); PeerRepo_Can_Update_And_Retrieve(); } - [Theory(Skip = "Setup to run in pipeline only")] - [Trait(Traits.TestType, Traits.E2EMongoDb)] - [MemberData(nameof(ModulesList))] - public void PeerRepo_All_Dbs_Can_Save_And_Retrieve(Module dbModule) + [Ignore("Setup to run in pipeline only")] + [Category(Traits.E2EMongoDb)] + [TestCase(nameof(ModulesList))] + public void PeerRepo_All_Dbs_Can_Save_And_Retrieve(Autofac.Module dbModule) { RegisterModules(dbModule); PeerRepo_Can_Save_And_Retrieve(); } - [Fact(Skip = "Microsoft DBs yet to be completed")] - [Trait(Traits.TestType, Traits.E2EMssql)] + [Ignore("Microsoft DBs yet to be completed")] + [Category(Traits.E2EMssql)] public void PeerRepo_EfCore_Dbs_Update_And_Retrieve() { var connectionStr = ContainerProvider.ConfigurationRoot @@ -144,8 +144,8 @@ public void PeerRepo_EfCore_Dbs_Update_And_Retrieve() PeerRepo_Can_Update_And_Retrieve(); } - [Fact(Skip = "Microsoft DBs yet to be completed")] - [Trait(Traits.TestType, Traits.E2EMssql)] + [Ignore("Microsoft DBs yet to be completed")] + [Category(Traits.E2EMssql)] public void PeerRepo_EfCore_Dbs_Can_Save_And_Retrieve() { var connectionStr = ContainerProvider.ConfigurationRoot @@ -168,7 +168,7 @@ private void CheckForDatabaseCreation() } } - private void RegisterModules(Module module) + private void RegisterModules(Autofac.Module module) { ContainerProvider.ConfigureContainerBuilder(); diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerSettingsTests.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerSettingsTests.cs index 310b0f7ce7..090d64d9c1 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerSettingsTests.cs +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerSettingsTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,24 +22,38 @@ #endregion using Autofac; +using Catalyst.Abstractions.Config; using Catalyst.Abstractions.P2P; +using Catalyst.Core.Lib.Config; +using Catalyst.Core.Modules.Dfs; using Catalyst.Protocol.Network; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NSubstitute; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.IntegrationTests.P2P { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class PeerSettingsTests : FileSystemBasedTest { - public PeerSettingsTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void CanResolveIPeerSettings() { ContainerProvider.ConfigureContainerBuilder(); + + var networkTypeProvider = Substitute.For(); + networkTypeProvider.NetworkType.Returns(NetworkType.Devnet); + ContainerProvider.ContainerBuilder.RegisterInstance(networkTypeProvider).As(); + + ContainerProvider.ContainerBuilder.RegisterModule(new DfsModule()); using (var scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName)) { diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerValidationIntegrationTest.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerValidationIntegrationTest.cs index 7452f7a9c8..3eb0445a7c 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerValidationIntegrationTest.cs +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/PeerValidationIntegrationTest.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,65 +25,87 @@ using System.Net; using System.Threading.Tasks; using Autofac; +using Catalyst.Abstractions.Cli; +using Catalyst.Abstractions.Config; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.Keystore; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.P2P.Discovery; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; -using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.EventLoop; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Abstractions.Types; +using Catalyst.Core.Lib.Cli; +using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.P2P; -using Catalyst.Core.Lib.P2P.IO.Transport.Channels; +using Catalyst.Core.Lib.P2P.Protocols; using Catalyst.Core.Modules.Cryptography.BulletProofs; +using Catalyst.Core.Modules.Dfs; using Catalyst.Core.Modules.Hashing; using Catalyst.Core.Modules.KeySigner; using Catalyst.Core.Modules.Keystore; +using Catalyst.Modules.Network.Dotnetty; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.P2P; +using Catalyst.Modules.Network.Dotnetty.P2P.IO.Transport.Channels; +using Catalyst.Protocol.Network; using Catalyst.TestUtils; using FluentAssertions; using NSubstitute; +using NUnit.Framework; using Serilog; -using Xunit; -using Xunit.Abstractions; namespace Catalyst.Core.Lib.Tests.IntegrationTests.P2P { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class PeerValidationIntegrationTest : FileSystemBasedTest { private IPeerService _peerService; - private IPeerChallenger _peerChallenger; - private readonly PeerSettings _peerSettings; + private IPeerChallengeRequest _peerChallengeRequest; + private PeerSettings _peerSettings; - public PeerValidationIntegrationTest(ITestOutputHelper output) : base(output) + [SetUp] + public void Init() { + Setup(TestContext.CurrentContext); + var logger = Substitute.For(); var keyRegistry = TestKeyRegistry.MockKeyRegistry(); ContainerProvider.ContainerBuilder.RegisterInstance(keyRegistry).As(); + ContainerProvider.ContainerBuilder.RegisterModule(new CoreLibProvider()); ContainerProvider.ContainerBuilder.RegisterModule(new KeystoreModule()); ContainerProvider.ContainerBuilder.RegisterModule(new KeySignerModule()); ContainerProvider.ContainerBuilder.RegisterModule(new HashingModule()); ContainerProvider.ContainerBuilder.RegisterModule(new BulletProofsModule()); + ContainerProvider.ContainerBuilder.RegisterModule(new DfsModule()); + ContainerProvider.ContainerBuilder.RegisterModule(new DotnettyNetworkModule()); + + ContainerProvider.ContainerBuilder.RegisterType().As(); + ContainerProvider.ContainerBuilder.RegisterType().As(); - _peerSettings = new PeerSettings(ContainerProvider.ConfigurationRoot); + var passwordManager = Substitute.For(); + passwordManager.PromptPassword(Arg.Is(PasswordRegistryTypes.DefaultNodePassword), Arg.Any()).Returns(TestPasswordReader.BuildSecureStringPassword("test")); + ContainerProvider.ContainerBuilder.RegisterInstance(passwordManager).As(); + + var networkTypeProvider = Substitute.For(); + networkTypeProvider.NetworkType.Returns(NetworkType.Devnet); + ContainerProvider.ContainerBuilder.RegisterInstance(networkTypeProvider).As(); - var peerSettings = - PeerIdHelper.GetPeerId("sender", _peerSettings.BindAddress, _peerSettings.Port).ToSubstitutedPeerSettings(); + _peerSettings = (PeerSettings) ContainerProvider.Container.Resolve(); ContainerProvider.ContainerBuilder.Register(c => { var peerClient = c.Resolve(); peerClient.StartAsync().ConfigureAwait(false).GetAwaiter().GetResult(); - return new PeerChallenger(logger, peerClient, peerSettings, 5); - }).As().SingleInstance(); - } + return new PeerChallengeRequest(logger, peerClient, _peerSettings, 10); + }).As().SingleInstance(); - private async Task Setup() - { - _peerChallenger = ContainerProvider.Container.Resolve(); + _peerChallengeRequest = ContainerProvider.Container.Resolve(); var eventLoopGroupFactoryConfiguration = new EventLoopGroupFactoryConfiguration { @@ -93,46 +115,47 @@ private async Task Setup() UdpClientHandlerWorkerThreads = 5 }; - var keySigner = Substitute.For(); - keySigner.Verify(Arg.Any(), Arg.Any(), default).ReturnsForAnyArgs(true); - var signature = Substitute.For(); - keySigner.Sign(Arg.Any(), default).ReturnsForAnyArgs(signature); + var keySigner = ContainerProvider.Container.Resolve(); // @@ - _peerService = new PeerService(new UdpServerEventLoopGroupFactory(eventLoopGroupFactoryConfiguration), + _peerService = new DotnettyPeerService( + new UdpServerEventLoopGroupFactory(eventLoopGroupFactoryConfiguration), new PeerServerChannelFactory(ContainerProvider.Container.Resolve(), ContainerProvider.Container.Resolve(), keySigner, ContainerProvider.Container.Resolve(), ContainerProvider.Container.Resolve()), - new DiscoveryHelper.DevDiscover(), + new DiscoveryHelper.DevDiscover(), ContainerProvider.Container.Resolve>(), _peerSettings, ContainerProvider.Container.Resolve(), ContainerProvider.Container.Resolve()); - await _peerService.StartAsync(); + _peerService.StartAsync().Wait(); } - // [Fact(Skip = "false")] - // [Trait(Traits.TestType, Traits.IntegrationTest)] + [TearDown] + public void TearDown() + { + this.Dispose(); + } + + // [Fact(Skip = "this wont work as it tries to connect to a real node!! We need to instantiate two sockets here")] // public async Task PeerChallenge_PeerIdentifiers_Expect_To_Succeed_Valid_IP_Port_PublicKey() // { - // await Setup().ConfigureAwait(false); - // var valid = await RunPeerChallengeTask(_peerSettings.PublicKey, _peerSettings.BindAddress, - // _peerSettings.Port).ConfigureAwait(false); - // - // valid.Should().BeTrue(); + // // await Setup().ConfigureAwait(false); + // // var valid = await RunPeerChallengeTask(_peerSettings.PublicKey, _peerSettings.BindAddress, + // // _peerSettings.Port).ConfigureAwait(false); + // // + // // valid.Should().BeTrue(); // } [Theory] - [Trait(Traits.TestType, Traits.IntegrationTest)] - [InlineData("ftqm5kpzpo7bvl6e53q5j6mmrjwupbbiuszpsopxvjodkkqqiusa", "92.207.178.198", 1574)] - [InlineData("fzqm5kpzpo7bvl5e53q5j6mmrjwupbbiuszpsopxvjodkkqqiusd", "198.51.100.3", 2524)] + [TestCase("42Bm3dA9xKjnMYp5CztnUYqEme4Y46suNCvKx6ueDhz", "92.207.178.198", 1574)] + [TestCase("483NWsFHJ5UrTRf8q6Ew3E8mD89i9cXyaxxFM1sSn4q1", "198.51.100.3", 2524)] public async Task PeerChallenge_PeerIdentifiers_Expect_To_Fail_IP_Port_PublicKey(string publicKey, string ip, int port) { - await Setup().ConfigureAwait(false); var valid = await RunPeerChallengeTask(publicKey, IPAddress.Parse(ip), port).ConfigureAwait(false); valid.Should().BeFalse(); @@ -140,13 +163,12 @@ public async Task PeerChallenge_PeerIdentifiers_Expect_To_Fail_IP_Port_PublicKey private async Task RunPeerChallengeTask(string publicKey, IPAddress ip, int port) { - Output.WriteLine(publicKey); - Output.WriteLine(ip.ToString()); - Output.WriteLine(port.ToString()); + TestContext.WriteLine(publicKey); + TestContext.WriteLine(ip.ToString()); + TestContext.WriteLine(port.ToString()); - var recipient = publicKey.BuildPeerIdFromBase32Key(ip, port); - - return await _peerChallenger.ChallengePeerAsync(recipient); + var recipient = MultiAddressHelper.GetAddress(publicKey, ip, port); + return await _peerChallengeRequest.ChallengePeerAsync(recipient); } protected override void Dispose(bool disposing) diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/ReputationSystem/ReputationManagerTests.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/ReputationSystem/ReputationManagerTests.cs index a88f8b5fde..20eda48f90 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/ReputationSystem/ReputationManagerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/P2P/ReputationSystem/ReputationManagerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,50 +24,57 @@ using Autofac; using Catalyst.Abstractions.Config; using Catalyst.Abstractions.P2P.ReputationSystem; +using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.P2P.Models; using Catalyst.Core.Lib.P2P.ReputationSystem; +using Catalyst.Core.Modules.Dfs; using Catalyst.Protocol.Peer; using Catalyst.TestUtils; using FluentAssertions; +using MultiFormats; using NSubstitute; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.IntegrationTests.P2P.ReputationSystem { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class ReputationManagerTests : FileSystemBasedTest { - private readonly IReputationManager _reputationManager; - private readonly ILifetimeScope _scope; + private IReputationManager _reputationManager; + private ILifetimeScope _scope; - public ReputationManagerTests(ITestOutputHelper output) : base(output) + [SetUp] + public void Init() { - ContainerProvider.ConfigureContainerBuilder(); + Setup(TestContext.CurrentContext); + ContainerProvider.ConfigureContainerBuilder(); + ContainerProvider.ContainerBuilder.RegisterModule(); _scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName); _reputationManager = _scope.Resolve(); } - private Peer SavePeerInRepo(PeerId pid, int initialRep = 100) + private Peer SavePeerInRepo(MultiAddress pid, int initialRep = 100) { var subbedPeer = new Peer { - PeerId = pid, + Address = pid, + KvmAddress = pid.GetKvmAddress(), Reputation = initialRep }; _reputationManager.PeerRepository.Add(subbedPeer); return subbedPeer; } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Can_Save_Increased_Peer() { - var pid = PeerIdHelper.GetPeerId("some_peer"); + var pid = MultiAddressHelper.GetAddress("some_peer"); var savedPeer = SavePeerInRepo(pid); var peerReputationChange = Substitute.For(); - peerReputationChange.PeerId.Returns(pid); + peerReputationChange.Address.Returns(pid.GetKvmAddress()); peerReputationChange.ReputationEvent.Returns(Substitute.For()); peerReputationChange.ReputationEvent.Amount.Returns(100); _reputationManager.OnNext(peerReputationChange); @@ -75,15 +82,14 @@ public void Can_Save_Increased_Peer() updatedSubbedPeer.Reputation.Should().Be(200); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Can_Save_Decreased_Peer() { - var pid = PeerIdHelper.GetPeerId("some_peer"); + var pid = MultiAddressHelper.GetAddress("some_peer"); var savedPeer = SavePeerInRepo(pid); var peerReputationChange = Substitute.For(); - peerReputationChange.PeerId.Returns(pid); + peerReputationChange.Address.Returns(pid.GetKvmAddress()); peerReputationChange.ReputationEvent.Returns(Substitute.For()); peerReputationChange.ReputationEvent.Amount.Returns(-100); _reputationManager.OnNext(peerReputationChange); @@ -91,15 +97,14 @@ public void Can_Save_Decreased_Peer() updatedSubbedPeer.Reputation.Should().Be(0); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Can_Save_Decreased_Peer_To_Negative_Number() { - var pid = PeerIdHelper.GetPeerId("some_peer"); + var pid = MultiAddressHelper.GetAddress("some_peer"); var savedPeer = SavePeerInRepo(pid); var peerReputationChange = Substitute.For(); - peerReputationChange.PeerId.Returns(pid); + peerReputationChange.Address.Returns(pid.GetKvmAddress()); peerReputationChange.ReputationEvent.Returns(Substitute.For()); peerReputationChange.ReputationEvent.Amount.Returns(-200); _reputationManager.OnNext(peerReputationChange); @@ -107,15 +112,14 @@ public void Can_Save_Decreased_Peer_To_Negative_Number() updatedSubbedPeer.Reputation.Should().Be(-100); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Can_Save_Increased_Peer_From_Negative_Number_To_Positive_Number() { - var pid = PeerIdHelper.GetPeerId("some_peer"); + var pid = MultiAddressHelper.GetAddress("some_peer"); var savedPeer = SavePeerInRepo(pid, -100); var peerReputationChange = Substitute.For(); - peerReputationChange.PeerId.Returns(pid); + peerReputationChange.Address.Returns(pid.GetKvmAddress()); peerReputationChange.ReputationEvent.Returns(Substitute.For()); peerReputationChange.ReputationEvent.Amount.Returns(200); _reputationManager.OnNext(peerReputationChange); diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Utils/DirectoryInfoExtensionsTests.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/Utils/DirectoryInfoExtensionsTests.cs index 18ea73b645..5afc943656 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Utils/DirectoryInfoExtensionsTests.cs +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/Utils/DirectoryInfoExtensionsTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,8 +25,8 @@ using Catalyst.Core.Lib.Extensions; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; + namespace Catalyst.Core.Lib.Tests.IntegrationTests.Utils { @@ -34,9 +34,13 @@ public sealed class DirectoryInfoExtensionsTests : FileSystemBasedTest { private readonly string _subDirectory = "subdir"; - public DirectoryInfoExtensionsTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + } - [Fact] + [Test] public void SubDirectoryInfo_Returns_Directory_Info_If_SubDirectory_Exists() { var dirInfo = FileSystem.GetCatalystDataDir(); @@ -48,7 +52,7 @@ public void SubDirectoryInfo_Returns_Directory_Info_If_SubDirectory_Exists() subDirInfo.Exists.Should().BeTrue(); } - [Fact] + [Test] public void SubDirectoryInfo_Returns_Directory_Info_If_SubDirectory_Doesnt_Exist() { var subDirInfo = FileSystem.GetCatalystDataDir().SubDirectoryInfo(_subDirectory); @@ -57,7 +61,7 @@ public void SubDirectoryInfo_Returns_Directory_Info_If_SubDirectory_Doesnt_Exist subDirInfo.Exists.Should().BeFalse(); } - [Fact] + [Test] public void SubDirectoryInfo_Has_Correct_Parent_Directory() { var parentDirInfo = FileSystem.GetCatalystDataDir(); diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Validators/TransactionValidatorTests.cs b/src/Catalyst.Core.Lib.Tests/IntegrationTests/Validators/TransactionValidatorTests.cs index 67885b7342..bb22f90c96 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Validators/TransactionValidatorTests.cs +++ b/src/Catalyst.Core.Lib.Tests/IntegrationTests/Validators/TransactionValidatorTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,59 +27,110 @@ using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Network; using Catalyst.Protocol.Transaction; -using Catalyst.Protocol.Wire; using FluentAssertions; using Google.Protobuf; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Core.Lib.Config; +using Catalyst.Abstractions.Config; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Validators; namespace Catalyst.Core.Lib.Tests.IntegrationTests.Validators { + [TestFixture] public sealed class TransactionValidatorTests { - [Fact] - public void ValidateTransactionSignature_will_pass_with_valid_transaction_signature() + private ITransactionConfig _transactionConfig; + private ICryptoContext _cryptoContext; + private ITransactionValidator _transactionValidator; + + private IPrivateKey _privateKey; + + private SigningContext _signingContext; + + [SetUp] + public void Init() { - var subbedLogger = Substitute.For(); - var cryptoContext = new FfiWrapper(); - var transactionValidator = new TransactionValidator(subbedLogger, cryptoContext); - - // build a valid transaction - var privateKey = cryptoContext.GeneratePrivateKey(); - - var validTransactionBroadcast = new TransactionBroadcast + _transactionConfig = new TransactionConfig(); + _cryptoContext = new FfiWrapper(); + _transactionValidator = new TransactionValidator(_cryptoContext, _transactionConfig, Substitute.For()); + _privateKey = _cryptoContext.GeneratePrivateKey(); + _signingContext = new SigningContext { - PublicEntries = - { - new PublicEntry - { - Base = new BaseEntry - { - SenderPublicKey = privateKey.GetPublicKey().Bytes.ToByteString() - } - } - } + NetworkType = NetworkType.Devnet, + SignatureType = SignatureType.TransactionPublic }; + } - var signingContext = new SigningContext + [Test] + public void ValidateTransactionSignature_Will_Pass_With_Valid_Transaction_Signature() + { + var validTransaction = new PublicEntry { - NetworkType = NetworkType.Devnet, - SignatureType = SignatureType.TransactionPublic + SenderAddress = _privateKey.GetPublicKey().Bytes.ToByteString(), + GasLimit = _transactionConfig.MinTransactionEntryGasLimit }; var signature = new Signature { - // sign an actual TransactionBroadcast object - RawBytes = cryptoContext.Sign(privateKey, validTransactionBroadcast.ToByteArray(), signingContext.ToByteArray()) + //Sign an actual PublicEntry object + RawBytes = _cryptoContext.Sign(_privateKey, validTransaction.ToByteArray(), _signingContext.ToByteArray()) .SignatureBytes.ToByteString(), - SigningContext = signingContext + SigningContext = _signingContext }; - - validTransactionBroadcast.Signature = signature; - var result = transactionValidator.ValidateTransaction(validTransactionBroadcast); + validTransaction.Signature = signature; + + var result = _transactionValidator.ValidateTransaction(validTransaction); result.Should().BeTrue(); } + + [Test] + public void ValidateTransactionSignature_Will_Fail_With_Invalid_Transaction_Signature() + { + var invalidTransaction = new PublicEntry + { + SenderAddress = _privateKey.GetPublicKey().Bytes.ToByteString(), + GasLimit = _transactionConfig.MinTransactionEntryGasLimit + }; + + var signature = new Signature + { + //Sign an actual PublicEntry with a different private key to create a invalid signature + RawBytes = _cryptoContext.Sign(_cryptoContext.GeneratePrivateKey(), invalidTransaction.ToByteArray(), _signingContext.ToByteArray()) + .SignatureBytes.ToByteString(), + SigningContext = _signingContext + }; + + invalidTransaction.Signature = signature; + + var result = _transactionValidator.ValidateTransaction(invalidTransaction); + result.Should().BeFalse(); + } + + [Test] + public void ValidateTransaction_Will_Fail_With_Less_Than_Minimum_Gas_Limit() + { + var invalidTransaction = new PublicEntry + { + SenderAddress = _privateKey.GetPublicKey().Bytes.ToByteString(), + //Set GasLimit to 1 less than minimum + GasLimit = _transactionConfig.MinTransactionEntryGasLimit - 1 + }; + + var signature = new Signature + { + RawBytes = _cryptoContext.Sign(_privateKey, invalidTransaction.ToByteArray(), _signingContext.ToByteArray()) + .SignatureBytes.ToByteString(), + SigningContext = _signingContext + }; + + invalidTransaction.Signature = signature; + + var result = _transactionValidator.ValidateTransaction(invalidTransaction); + result.Should().BeFalse(); + } } } diff --git a/src/Catalyst.Core.Lib.Tests/LicenseHeaderTests.cs b/src/Catalyst.Core.Lib.Tests/LicenseHeaderTests.cs index ce82a2d56f..25b6c5beef 100644 --- a/src/Catalyst.Core.Lib.Tests/LicenseHeaderTests.cs +++ b/src/Catalyst.Core.Lib.Tests/LicenseHeaderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,17 +29,15 @@ using System.Threading.Tasks; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests { public class LicenseHeaderTests : FileSystemBasedTest { - public LicenseHeaderTests(ITestOutputHelper output) : base(output) { } private const string LicenseHeaderFileName = "COPYING"; - [Fact] + [Test] public static async Task All_Cs_Files_Should_Have_License_Header() { var declaringTypeAssembly = MethodBase.GetCurrentMethod().DeclaringType?.Assembly; diff --git a/src/Catalyst.Core.Lib.Tests/ModuleNamesTests.cs b/src/Catalyst.Core.Lib.Tests/ModuleNamesTests.cs index b2ccf905eb..c1ee74d89a 100644 --- a/src/Catalyst.Core.Lib.Tests/ModuleNamesTests.cs +++ b/src/Catalyst.Core.Lib.Tests/ModuleNamesTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,13 +26,13 @@ using Catalyst.Abstractions.Enumerator; using Catalyst.Abstractions.Types; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests { public static class ModuleNamesTests { - [Fact] + [Test] public static void All_should_return_all_declared_names() { var allModuleNames = Enumeration.GetAll().Select(m => m.Name); diff --git a/src/Catalyst.Core.Lib.Tests/TestCatalystNode.cs b/src/Catalyst.Core.Lib.Tests/TestCatalystNode.cs index 98dc87e2bc..8cd001bd81 100644 --- a/src/Catalyst.Core.Lib.Tests/TestCatalystNode.cs +++ b/src/Catalyst.Core.Lib.Tests/TestCatalystNode.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,7 +32,7 @@ using Catalyst.Core.Lib.Config; using Catalyst.Protocol.Network; using Catalyst.TestUtils; -using Xunit.Abstractions; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests { @@ -50,15 +50,15 @@ public class TestCatalystNode : FileSystemBasedTest, ICatalystNode private readonly ContainerProvider _configProvider; - public TestCatalystNode(string name, ITestOutputHelper output) - : base(output, new[] + public TestCatalystNode(string name) + : base(new[] { Constants.NetworkConfigFile(NetworkType.Devnet), Constants.SerilogJsonConfigFile }.Select(f => Path.Combine(Constants.ConfigSubFolder, f))) { Name = name; - _configProvider = new ContainerProvider(_configFilesUsed, FileSystem, output); + _configProvider = new ContainerProvider(_configFilesUsed, FileSystem, TestContext.CurrentContext); } public IConsensus Consensus => _catalystNode.Consensus; diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Config/ConfigValueParserTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Config/ConfigValueParserTests.cs new file mode 100644 index 0000000000..f9de23a5f1 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Config/ConfigValueParserTests.cs @@ -0,0 +1,83 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Net; +using Catalyst.Core.Lib.Config; +using FluentAssertions; +using NUnit.Framework; +using Microsoft.Extensions.Configuration; + +namespace Catalyst.Core.Lib.Tests.UnitTests.Config +{ + public sealed class ConfigValueParserTests + { + public ConfigValueParserTests() + { + _ipEndpoint1 = IPEndPoint.Parse("127.0.0.1:5052"); + _ipEndpoint2 = IPEndPoint.Parse("127.0.0.1:5053"); + _sectionName = "DnsServers"; + var peerConfig = new List> + { + new KeyValuePair("CatalystNodeConfiguration:Peer:" + _sectionName + ":0", _ipEndpoint1.ToString()), + new KeyValuePair("CatalystNodeConfiguration:Peer:" + _sectionName + ":1", _ipEndpoint2.ToString()) + }; + + _configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(peerConfig).Build(); + } + + private readonly IConfigurationRoot _configurationRoot; + private readonly string _sectionName; + private readonly IPEndPoint _ipEndpoint1; + private readonly IPEndPoint _ipEndpoint2; + + [Test] + public void ConfigValueParser_Can_Parse_Multiple_IpEndpoints() + { + var endPoints = ConfigValueParser.GetIpEndpointArrValues(_configurationRoot, _sectionName); + endPoints.Should().HaveCount(2); + endPoints.Should().Contain(_ipEndpoint1); + endPoints.Should().Contain(_ipEndpoint2); + } + + [Test] + public void ConfigValueParser_Can_Parse_Multiple_IpEndpoints_As_Strings() + { + var endPoints = ConfigValueParser.GetStringArrValues(_configurationRoot, _sectionName); + endPoints.Should().HaveCount(2); + endPoints.Should().Contain(_ipEndpoint1.ToString()); + endPoints.Should().Contain(_ipEndpoint2.ToString()); + } + + [Test] + public void ConfigValueParser_Can_Parse_Empty_Config() + { + var peerConfig = new List>(); + + var configurationRoot = new ConfigurationBuilder().AddInMemoryCollection(peerConfig).Build(); + + var endPoints = ConfigValueParser.GetIpEndpointArrValues(configurationRoot, _sectionName); + endPoints.Should().HaveCount(0); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Config/ValidatorsConfigTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Config/ValidatorsConfigTests.cs new file mode 100644 index 0000000000..61f85ad81e --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Config/ValidatorsConfigTests.cs @@ -0,0 +1,121 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Autofac; +using Autofac.Configuration; +using Catalyst.Abstractions.Config; +using Catalyst.Abstractions.Contract; +using Catalyst.Abstractions.Validators; +using Catalyst.Core.Lib.Config; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.Validators; +using Catalyst.Core.Modules.Kvm.Validators; +using FluentAssertions; +using Microsoft.Extensions.Configuration; +using NSubstitute; +using NUnit.Framework; + +namespace Catalyst.Core.Lib.Tests.UnitTests.Config +{ + [TestFixture] + public class ValidatorsConfigTests + { + private readonly string _configJson = @"{ + ""validators"": { + ""multi"": { + ""0"": { + ""list"": [ ""0x1a2149b4df5cbac970bc38fecc5237800c688c8b"" ] + }, + ""900"": { + ""contract"": ""0x79dd7e4c1b9adb07f71b54dba2d54db2fa549de3"" + } + } + } + }"; + + private IContainer _container; + + [SetUp] + public void Init() + { + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddJsonStream(_configJson.ToMemoryStream()); + + var config = configurationBuilder.Build(); + var configModule = new ConfigurationModule(config); + + var containerBuilder = new ContainerBuilder(); + containerBuilder.RegisterInstance(config); + containerBuilder.RegisterModule(configModule); + + containerBuilder.RegisterType().As().SingleInstance(); + + //Readers + containerBuilder.RegisterType().As(); + containerBuilder.RegisterType().As(); + + //Contract + containerBuilder.RegisterInstance(Substitute.For()).As(); + + //Store + containerBuilder.RegisterType().As().SingleInstance(); + + _container = containerBuilder.Build(); + } + + [Test] + public void Can_Parse_Validator_Json_File() + { + using (var scope = _container.BeginLifetimeScope()) + { + var validatorSetStore = scope.Resolve(); + var listSet = validatorSetStore.Get(0); + var contractSet = validatorSetStore.Get(900); + + listSet.Should().BeOfType(); + contractSet.Should().BeOfType(); + } + } + + [Test] + public void ValidatorSetStore_Can_Return_Correct_Validation_Set_At_Start_Block() + { + using (var scope = _container.BeginLifetimeScope()) + { + var validatorSetStore = scope.Resolve(); + + var listSet = validatorSetStore.Get(0); + listSet.Should().BeAssignableTo(); + + var listSet2 = validatorSetStore.Get(450); + listSet2.Should().BeAssignableTo(); + + var contractSet = validatorSetStore.Get(900); + contractSet.Should().BeAssignableTo(); + + var contractSet2 = validatorSetStore.Get(1350); + contractSet2.Should().BeAssignableTo(); + } + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/CertTest.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/CertTest.cs new file mode 100644 index 0000000000..72131a271c --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/CertTest.cs @@ -0,0 +1,113 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Security; +using System.Threading.Tasks; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.Config; +using Catalyst.Core.Modules.Keystore; +using Catalyst.TestUtils; +using Makaretu.Dns; +using MultiFormats; +using NSubstitute; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.X509.Extension; +using NUnit.Framework; + + +namespace Catalyst.Core.Lib.Tests.UnitTests.Cryptography +{ + public class CertTest : FileSystemBasedTest + { + private KeyStoreService _keyStoreService; + + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + var keyChain = Substitute.For(); + _keyStoreService = new KeyStoreService(keyChain, new KeyFileStore(new RepositoryOptions(FileSystem, Constants.DfsDataSubDir))); + var securePassword = new SecureString(); + + "mypassword".ToList().ForEach(c => securePassword.AppendChar(c)); + + securePassword.MakeReadOnly(); + _keyStoreService.SetPassphraseAsync(securePassword).ConfigureAwait(false); + } + + [Test] + public async Task Create_Rsa() + { + var key = await _keyStoreService.CreateAsync("alice", "rsa", 512); + try + { + var cert = await _keyStoreService.CreateBcCertificateAsync(key.Name); + Assert.AreEqual($"CN={key.Id},OU=keystore,O=ipfs", cert.SubjectDN.ToString()); + var ski = new SubjectKeyIdentifierStructure( + cert.GetExtensionValue(X509Extensions.SubjectKeyIdentifier)); + Assert.AreEqual(key.Id.ToBase58(), ski.GetKeyIdentifier().ToBase58()); + } + finally + { + await _keyStoreService.RemoveAsync("alice"); + } + } + + [Test] + public async Task Create_Secp256k1() + { + var key = await _keyStoreService.CreateAsync("alice", "secp256k1", 0); + try + { + var cert = await _keyStoreService.CreateBcCertificateAsync("alice"); + Assert.AreEqual($"CN={key.Id},OU=keystore,O=ipfs", cert.SubjectDN.ToString()); + var ski = new SubjectKeyIdentifierStructure( + cert.GetExtensionValue(X509Extensions.SubjectKeyIdentifier)); + Assert.AreEqual(key.Id.ToBase58(), ski.GetKeyIdentifier().ToBase58()); + } + finally + { + await _keyStoreService.RemoveAsync("alice"); + } + } + + [Test] + public async Task Create_Ed25519() + { + var key = await _keyStoreService.CreateAsync("alice", "ed25519", 0); + try + { + var cert = await _keyStoreService.CreateBcCertificateAsync("alice"); + Assert.AreEqual($"CN={key.Id},OU=keystore,O=ipfs", cert.SubjectDN.ToString()); + var ski = new SubjectKeyIdentifierStructure( + cert.GetExtensionValue(X509Extensions.SubjectKeyIdentifier)); + Assert.AreEqual(key.Id.ToBase58(), ski.GetKeyIdentifier().ToBase58()); + } + finally + { + await _keyStoreService.RemoveAsync("alice"); + } + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/CmsTest.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/CmsTest.cs new file mode 100644 index 0000000000..2a464e345c --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/CmsTest.cs @@ -0,0 +1,180 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Security; +using System.Text; +using System.Threading.Tasks; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.Config; +using Catalyst.Core.Modules.Keystore; +using Catalyst.TestUtils; +using FluentAssertions; +using Makaretu.Dns; +using NSubstitute; +using NUnit.Framework; + + +namespace Catalyst.Core.Lib.Tests.UnitTests.Cryptography +{ + public class CmsTest : FileSystemBasedTest + { + private KeyStoreService _keyStoreService; + + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + var keyChain = Substitute.For(); + _keyStoreService = new KeyStoreService(keyChain, new KeyFileStore(new RepositoryOptions(FileSystem, Constants.DfsDataSubDir))); + var securePassword = new SecureString(); + + "mypassword".ToList().ForEach(c => securePassword.AppendChar(c)); + + securePassword.MakeReadOnly(); + _keyStoreService.SetPassphraseAsync(securePassword).ConfigureAwait(false); + } + + [Test] + public async Task ReadCms() + { + string aliceKid = "QmNzBqPwp42HZJccsLtc4ok6LjZAspckgs2du5tTmjPfFA"; + string alice = @"-----BEGIN ENCRYPTED PRIVATE KEY----- +MIICxjBABgkqhkiG9w0BBQ0wMzAbBgkqhkiG9w0BBQwwDgQIMhYqiVoLJMICAggA +MBQGCCqGSIb3DQMHBAhU7J9bcJPLDQSCAoDzi0dP6z97wJBs3jK2hDvZYdoScknG +QMPOnpG1LO3IZ7nFha1dta5liWX+xRFV04nmVYkkNTJAPS0xjJOG9B5Hm7wm8uTd +1rOaYKOW5S9+1sD03N+fAx9DDFtB7OyvSdw9ty6BtHAqlFk3+/APASJS12ak2pg7 +/Ei6hChSYYRS9WWGw4lmSitOBxTmrPY1HmODXkR3txR17LjikrMTd6wyky9l/u7A +CgkMnj1kn49McOBJ4gO14c9524lw9OkPatyZK39evFhx8AET73LrzCnsf74HW9Ri +dKq0FiKLVm2wAXBZqdd5ll/TPj3wmFqhhLSj/txCAGg+079gq2XPYxxYC61JNekA +ATKev5zh8x1Mf1maarKN72sD28kS/J+aVFoARIOTxbG3g+1UbYs/00iFcuIaM4IY +zB1kQUFe13iWBsJ9nfvN7TJNSVnh8NqHNbSg0SdzKlpZHHSWwOUrsKmxmw/XRVy/ +ufvN0hZQ3BuK5MZLixMWAyKc9zbZSOB7E7VNaK5Fmm85FRz0L1qRjHvoGcEIhrOt +0sjbsRvjs33J8fia0FF9nVfOXvt/67IGBKxIMF9eE91pY5wJNwmXcBk8jghTZs83 +GNmMB+cGH1XFX4cT4kUGzvqTF2zt7IP+P2cQTS1+imKm7r8GJ7ClEZ9COWWdZIcH +igg5jozKCW82JsuWSiW9tu0F/6DuvYiZwHS3OLiJP0CuLfbOaRw8Jia1RTvXEH7m +3N0/kZ8hJIK4M/t/UAlALjeNtFxYrFgsPgLxxcq7al1ruG7zBq8L/G3RnkSjtHqE +cn4oisOvxCprs4aM9UVjtZTCjfyNpX8UWwT1W3rySV+KQNhxuMy3RzmL +-----END ENCRYPTED PRIVATE KEY----- +"; + var key = await _keyStoreService.ImportAsync("alice", alice, "mypassword".ToArray()); + try + { + Assert.AreEqual(aliceKid, key.Id.ToString()); + + var cipher = Convert.FromBase64String(@" +MIIBcwYJKoZIhvcNAQcDoIIBZDCCAWACAQAxgfowgfcCAQAwYDBbMQ0wCwYDVQQK +EwRpcGZzMREwDwYDVQQLEwhrZXlzdG9yZTE3MDUGA1UEAxMuUW1OekJxUHdwNDJI +WkpjY3NMdGM0b2s2TGpaQXNwY2tnczJkdTV0VG1qUGZGQQIBATANBgkqhkiG9w0B +AQEFAASBgLKXCZQYmMLuQ8m0Ex/rr3KNK+Q2+QG1zIbIQ9MFPUNQ7AOgGOHyL40k +d1gr188EHuiwd90PafZoQF9VRSX9YtwGNqAE8+LD8VaITxCFbLGRTjAqeOUHR8cO +knU1yykWGkdlbclCuu0NaAfmb8o0OX50CbEKZB7xmsv8tnqn0H0jMF4GCSqGSIb3 +DQEHATAdBglghkgBZQMEASoEEP/PW1JWehQx6/dsLkp/Mf+gMgQwFM9liLTqC56B +nHILFmhac/+a/StQOKuf9dx5qXeGvt9LnwKuGGSfNX4g+dTkoa6N +"); + var plain = await _keyStoreService.ReadProtectedDataAsync(cipher); + var plainText = Encoding.UTF8.GetString(plain); + Assert.AreEqual("This is a message from Alice to Bob", plainText); + } + finally + { + await _keyStoreService.RemoveAsync("alice"); + } + } + + [Test] + public void ReadCms_FailsWithoutKey() + { + var cipher = Convert.FromBase64String(@" +MIIBcwYJKoZIhvcNAQcDoIIBZDCCAWACAQAxgfowgfcCAQAwYDBbMQ0wCwYDVQQK +EwRpcGZzMREwDwYDVQQLEwhrZXlzdG9yZTE3MDUGA1UEAxMuUW1OekJxUHdwNDJI +WkpjY3NMdGM0b2s2TGpaQXNwY2tnczJkdTV0VG1qUGZGQQIBATANBgkqhkiG9w0B +AQEFAASBgLKXCZQYmMLuQ8m0Ex/rr3KNK+Q2+QG1zIbIQ9MFPUNQ7AOgGOHyL40k +d1gr188EHuiwd90PafZoQF9VRSX9YtwGNqAE8+LD8VaITxCFbLGRTjAqeOUHR8cO +knU1yykWGkdlbclCuu0NaAfmb8o0OX50CbEKZB7xmsv8tnqn0H0jMF4GCSqGSIb3 +DQEHATAdBglghkgBZQMEASoEEP/PW1JWehQx6/dsLkp/Mf+gMgQwFM9liLTqC56B +nHILFmhac/+a/StQOKuf9dx5qXeGvt9LnwKuGGSfNX4g+dTkoa6N +"); + ExceptionAssert.Throws(() => + { + var _ = _keyStoreService.ReadProtectedDataAsync(cipher).Result; + }); + } + + [Test] + public async Task CreateCms_Rsa() + { + var _ = await _keyStoreService.CreateAsync("alice", "rsa", 512); + try + { + var data = new byte[] {1, 2, 3, 4}; + var cipher = await _keyStoreService.CreateProtectedDataAsync("alice", data); + var plain = await _keyStoreService.ReadProtectedDataAsync(cipher); + Assert.AreEqual(data, plain); + } + finally + { + await _keyStoreService.RemoveAsync("alice"); + } + } + + [Test] + public async Task CreateCms_Secp256k1() + { + var _ = await _keyStoreService.CreateAsync("alice", "secp256k1", 0); + try + { + var data = new byte[] {1, 2, 3, 4}; + var cipher = await _keyStoreService.CreateProtectedDataAsync("alice", data); + var plain = await _keyStoreService.ReadProtectedDataAsync(cipher); + Assert.AreEqual(data, plain); + } + finally + { + await _keyStoreService.RemoveAsync("alice"); + } + } + + // [Test] + // [Skip("NYI")] + // public async Task CreateCms_Ed25519() + // { + // var ipfs = TestFixture.Ipfs; + // var keychain = await ipfs.KeyChainAsync(); + // var key = await ipfs.Key.CreateAsync("alice", "ed25519", 0); + // try + // { + // var data = new byte[] {1, 2, 3, 4}; + // var cipher = await keychain.CreateProtectedDataAsync("alice", data); + // var plain = await keychain.ReadProtectedDataAsync(cipher); + // CollectionAssert.AreEqual(data, plain); + // } + // finally + // { + // await ipfs.Key.RemoveAsync("alice"); + // } + // } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/ConsolePasswordReaderTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/ConsolePasswordReaderTests.cs index fa6f32c780..cbba9d958d 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/ConsolePasswordReaderTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/ConsolePasswordReaderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,17 +27,18 @@ using Catalyst.Core.Lib.Cryptography; using FluentAssertions; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Cryptography { public class ConsolePasswordReaderTests { - private readonly IUserInput _userInput; - private readonly IUserOutput _userOutput; - private readonly ConsolePasswordReader _consolePasswordReader; + private IUserInput _userInput; + private IUserOutput _userOutput; + private ConsolePasswordReader _consolePasswordReader; - public ConsolePasswordReaderTests() + [SetUp] + public void Init() { _userInput = Substitute.For(); _userOutput = Substitute.For(); @@ -45,7 +46,7 @@ public ConsolePasswordReaderTests() _consolePasswordReader = new ConsolePasswordReader(_userOutput, _userInput); } - [Fact] + [Test] public void ReadSecurePassword_Should_Prompt_Context_And_Get_Password_From_Console() { var prompt = "hello give me a password"; @@ -70,7 +71,7 @@ public void ReadSecurePassword_Should_Prompt_Context_And_Get_Password_From_Conso } } - [Fact] + [Test] public void ReadSecurePassword_Should_Not_Accept_Password_Above_MaxLength_Chars() { var prompt = "hello give me a password"; diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/CryptographyTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/CryptographyTests.cs index e51e71ee35..d67135d9ec 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/CryptographyTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/CryptographyTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,7 +29,7 @@ using Catalyst.Core.Modules.Cryptography.BulletProofs.Types; using FluentAssertions; using Nethereum.Hex.HexConvertors.Extensions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Cryptography { @@ -39,14 +39,14 @@ public sealed class CryptographyTests private readonly ICryptoContext _context; - [Fact] + [Test] public void TestGeneratePrivateKey() { var privateKey = _context.GeneratePrivateKey(); privateKey.Should().BeOfType(typeof(PrivateKey)); } - [Fact] + [Test] public void TestFailureSigningVerification() { var key1 = _context.GeneratePrivateKey(); @@ -64,7 +64,7 @@ public void TestFailureSigningVerification() .Should().BeFalse("signature should not verify with incorrect key"); } - [Fact] + [Test] public void TestPublicKeyFromPrivateKey() { var privateKey = _context.GeneratePrivateKey(); @@ -73,7 +73,7 @@ public void TestPublicKeyFromPrivateKey() publicKey.Should().NotBeNull(" a valid public key should be created from a private key"); } - [Fact] + [Test] public void TestSigningVerification() { var privateKey = _context.GeneratePrivateKey(); @@ -85,7 +85,7 @@ public void TestSigningVerification() .Should().BeTrue("signature generated with private key should verify with corresponding public key"); } - [Fact] + [Test] public void TestVerifyWithImportedPublicKey() { var privateKey = _context.GeneratePrivateKey(); @@ -102,7 +102,7 @@ public void TestVerifyWithImportedPublicKey() .BeTrue("signature should verify with imported public key"); } - [Fact] + [Test] public void Does_Verify_Fail_With_Incorrect_Signing_Context() { var privateKey = _context.GeneratePrivateKey(); @@ -116,7 +116,7 @@ public void Does_Verify_Fail_With_Incorrect_Signing_Context() .BeFalse("signature should not verify with incorrect signing context"); } - [Fact] + [Test] public void Can_Throw_Signature_Exception_On_Invalid_Signature() { IPrivateKey privateKey = _context.GeneratePrivateKey(); @@ -130,19 +130,19 @@ public void Can_Throw_Signature_Exception_On_Invalid_Signature() } [Theory] - [InlineData("616263", + [TestCase("616263", "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "", true)] - [InlineData("616263", + [TestCase("616263", "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "a", false)] - [InlineData("616261", + [TestCase("616261", "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "", false)] - [InlineData("616263", + [TestCase("616263", "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", "0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772", "", false)] - [InlineData("616263", + [TestCase("616263", "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083405", "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "", false)] public void Verify_Validates_Correct_Test_Vector(string message, @@ -162,25 +162,25 @@ public void Verify_Validates_Correct_Test_Vector(string message, _context.Verify(signature, messageBytes, contextBytes).Should().Be(expectedResult); } - [Fact] + [Test] public void Is_PrivateKey_Length_Positive() { _context.PrivateKeyLength.Should().BePositive(); } - [Fact] + [Test] public void Is_PublicKey_Length_Positive() { _context.PublicKeyLength.Should().BePositive(); } - [Fact] + [Test] public void Is_Signature_Length_Positive() { _context.SignatureLength.Should().BePositive(); } - [Fact] + [Test] public void Is_Signature_Context_Max_Length_Positive() { _context.SignatureContextMaxLength.Should().BePositive(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/IsaacRandomTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/IsaacRandomTests.cs index 16466f960e..208bcb357a 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/IsaacRandomTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/IsaacRandomTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,16 +25,15 @@ using Catalyst.Abstractions.Cryptography; using Catalyst.Core.Lib.Cryptography; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Cryptography { public class IsaacRandomTests { - [Theory] - [InlineData(1000)] - [InlineData(10000)] - [InlineData(100000)] + [TestCase(1000u)] + [TestCase(10000u)] + [TestCase(100000u)] public void Test_Deterministic_Algorithm(uint sequenceSize) { var seed = CorrelationId.GenerateCorrelationId().ToString(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/PasswordManagerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/PasswordManagerTests.cs index 94c80bf383..bac375d739 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/PasswordManagerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/PasswordManagerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,18 +28,19 @@ using Catalyst.Core.Lib.Cryptography; using FluentAssertions; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Cryptography { public sealed class PasswordManagerTests : IDisposable { - private readonly IPasswordRegistry _passwordRegistry; - private readonly IPasswordReader _passwordReader; - private readonly PasswordManager _passwordManager; - private readonly SecureString _secureString; + private IPasswordRegistry _passwordRegistry; + private IPasswordReader _passwordReader; + private PasswordManager _passwordManager; + private SecureString _secureString; - public PasswordManagerTests() + [SetUp] + public void Init() { _passwordRegistry = Substitute.For(); _passwordReader = Substitute.For(); @@ -49,7 +50,20 @@ public PasswordManagerTests() _passwordManager = new PasswordManager(_passwordReader, _passwordRegistry); } - [Fact] + [Test] + public void PromptPassword_Should_Return_Password() + { + var registryType = PasswordRegistryTypes.CertificatePassword; + _passwordReader.ReadSecurePassword(Arg.Any()).Returns(_secureString); + + var retrievedPassword = _passwordManager.PromptPassword(registryType); + + _passwordReader.Received(1).ReadSecurePassword(Arg.Is(s => s.Contains(registryType.Name))); + + retrievedPassword.Should().Be(_secureString); + } + + [Test] public void ReadAndAddPasswordToRegistry_Should_Prompt_And_Add_Non_Null_Passwords() { _passwordReader.ReadSecurePassword().ReturnsForAnyArgs(_secureString); @@ -65,12 +79,12 @@ public void ReadAndAddPasswordToRegistry_Should_Prompt_And_Add_Non_Null_Password retrievedPassword.Should().Be(_secureString); } - [Fact] + [Test] public void ReadAndAddPasswordToRegistry_Should_Prompt_And_Not_Add_Null_Passwords() { _passwordReader.ReadSecurePassword().ReturnsForAnyArgs(_secureString); - var registryType = PasswordRegistryTypes.IpfsPassword; + var registryType = PasswordRegistryTypes.DefaultNodePassword; _passwordReader.ReadSecurePassword(Arg.Any()).Returns((SecureString) null); var retrievedPassword = _passwordManager.RetrieveOrPromptAndAddPasswordToRegistry(registryType); @@ -81,7 +95,7 @@ public void ReadAndAddPasswordToRegistry_Should_Prompt_And_Not_Add_Null_Password retrievedPassword.Should().BeNull(); } - [Fact] + [Test] public void AddPasswordToRegistry_Should_Add_Passwords_To_The_Correct_Registry() { _passwordReader.ReadSecurePassword().ReturnsForAnyArgs(_secureString); @@ -95,7 +109,7 @@ public void AddPasswordToRegistry_Should_Add_Passwords_To_The_Correct_Registry() allGood.Should().BeTrue(); } - [Fact] + [Test] public void RetrieveOrPromptPassword_When_Pass_In_Registry_Should_Not_Prompt_Password_From_Console() { var registryType = PasswordRegistryTypes.DefaultNodePassword; @@ -108,10 +122,10 @@ public void RetrieveOrPromptPassword_When_Pass_In_Registry_Should_Not_Prompt_Pas retrievedPassword.Should().Be(_secureString); } - [Fact] + [Test] public void RetrieveOrPromptPassword_When_Pass_Not_In_Registry_Should_Prompt_Password_From_Console() { - var registryType = PasswordRegistryTypes.IpfsPassword; + var registryType = PasswordRegistryTypes.DefaultNodePassword; _passwordRegistry.GetItemFromRegistry( Arg.Is(registryType)).Returns((SecureString) null); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/PasswordRepeaterTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/PasswordRepeaterTests.cs new file mode 100644 index 0000000000..400c2314bf --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/PasswordRepeaterTests.cs @@ -0,0 +1,104 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Cli; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Types; +using Catalyst.Core.Lib.Cryptography; +using Catalyst.TestUtils; +using FluentAssertions; +using NSubstitute; +using NSubstitute.ExceptionExtensions; +using NUnit.Framework; +using System; +using System.Security; +using System.Text; +using System.Threading.Tasks; + +namespace Catalyst.Core.Lib.Tests.UnitTests.Cryptography +{ + public class PasswordRepeaterTests + { + private IPasswordRepeater _passwordRepeater; + private IKeyApi _keyApi; + private IUserOutput _userOutput; + private IPasswordManager _passwordManager; + private SecureString _password; + + [SetUp] + public void Init() + { + _keyApi = Substitute.For(); + _userOutput = Substitute.For(); + + _password = TestPasswordReader.BuildSecureStringPassword("test"); + + _passwordManager = Substitute.For(); + _passwordRepeater = new NodePasswordRepeater(_keyApi, _passwordManager, _userOutput); + } + + [Test] + public async Task PasswordRepeater_Should_Return_Password() + { + _passwordManager.PromptPassword(Arg.Is(PasswordRegistryTypes.DefaultNodePassword), Arg.Any()).Returns(_password); + + var password = await _passwordRepeater.PromptAndReceiveAsync(); + + password.Should().Be(_password); + } + + [Test] + public async Task PasswordRepeater_Should_Add_Password_To_Password_Registry() + { + _passwordManager.PromptPassword(Arg.Is(PasswordRegistryTypes.DefaultNodePassword), Arg.Any()).Returns(_password); + + await _passwordRepeater.PromptAndAddPasswordToRegistryAsync(); + + _passwordManager.Received(1).AddPasswordToRegistry(Arg.Is(PasswordRegistryTypes.DefaultNodePassword), Arg.Is(_password)); + } + + [Test] + public async Task PasswordRepeater_Should_Ask_For_Password_When_Password_Is_Invalid() + { + var attempts = 0; + var maxAttempts = 3; + _keyApi.SetPassphraseAsync(Arg.Is(_password)).Throws(); + _passwordManager.PromptPassword(Arg.Is(PasswordRegistryTypes.DefaultNodePassword), Arg.Any()).Returns(_password).AndDoes(x => + { + //Attempt 3 wrong password, and then the correct password. + if (attempts >= maxAttempts) + { + _keyApi.SetPassphraseAsync(Arg.Is(_password)).Returns(Task.CompletedTask); + } + attempts++; + }); + + var password = await _passwordRepeater.PromptAndReceiveAsync(); + + _userOutput.Received(maxAttempts).WriteLine($"Invalid node password, please try again."); + + password.Should().Be(_password); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/Rfc8410Test.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/Rfc8410Test.cs new file mode 100644 index 0000000000..a8fd2fc808 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/Rfc8410Test.cs @@ -0,0 +1,111 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Security; +using System.Threading.Tasks; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.Config; +using Catalyst.Core.Modules.Keystore; +using Catalyst.TestUtils; +using Makaretu.Dns; +using MultiFormats; +using NSubstitute; +using Org.BouncyCastle.Crypto.Parameters; +using NUnit.Framework; + + +namespace Catalyst.Core.Lib.Tests.UnitTests.Cryptography +{ + public class Rfc8410Test : FileSystemBasedTest + { + private KeyStoreService _keyStoreService; + + [SetUp] + public void Init() + { + var keyChain = Substitute.For(); + _keyStoreService = new KeyStoreService(keyChain, new KeyFileStore(new RepositoryOptions(FileSystem, Constants.DfsDataSubDir))); + var securePassword = new SecureString(); + + "mypassword".ToList().ForEach(c => securePassword.AppendChar(c)); + + securePassword.MakeReadOnly(); + _keyStoreService.SetPassphraseAsync(securePassword).ConfigureAwait(false); + } + + [Test] + public async Task ReadPrivateKey() + { + const string alice1 = @"-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +-----END PRIVATE KEY----- +"; + var _ = await _keyStoreService.ImportAsync("alice1", alice1); + try + { + var priv = (Ed25519PrivateKeyParameters) await _keyStoreService.GetPrivateKeyAsync("alice1"); + Assert.True(priv.IsPrivate); + Assert.AreEqual("d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842", + priv.GetEncoded().ToHexString()); + + var pub = priv.GeneratePublicKey(); + Assert.False(pub.IsPrivate); + Assert.AreEqual("19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1", + pub.GetEncoded().ToHexString()); + } + finally + { + await _keyStoreService.RemoveAsync("alice1"); + } + } + + [Test] + public async Task ReadPrivateAndPublicKey() + { + const string alice1 = @"-----BEGIN PRIVATE KEY----- +MHICAQEwBQYDK2VwBCIEINTuctv5E1hK1bbY8fdp+K06/nwoy/HU++CXqI9EdVhC +oB8wHQYKKoZIhvcNAQkJFDEPDA1DdXJkbGUgQ2hhaXJzgSEAGb9ECWmEzf6FQbrB +Z9w7lshQhqowtrbLDFw4rXAxZuE= +-----END PRIVATE KEY----- +"; + var _ = await _keyStoreService.ImportAsync("alice1", alice1); + try + { + var priv = (Ed25519PrivateKeyParameters) await _keyStoreService.GetPrivateKeyAsync("alice1"); + Assert.True(priv.IsPrivate); + Assert.AreEqual("d4ee72dbf913584ad5b6d8f1f769f8ad3afe7c28cbf1d4fbe097a88f44755842", + priv.GetEncoded().ToHexString()); + + var pub = priv.GeneratePublicKey(); + Assert.False(pub.IsPrivate); + Assert.AreEqual("19bf44096984cdfe8541bac167dc3b96c85086aa30b6b6cb0c5c38ad703166e1", + pub.GetEncoded().ToHexString()); + } + finally + { + await _keyStoreService.RemoveAsync("alice1"); + } + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/SecureStringExtensionsTest.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/SecureStringExtensionsTest.cs new file mode 100644 index 0000000000..85ee45e77b --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Cryptography/SecureStringExtensionsTest.cs @@ -0,0 +1,56 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Security; +using Catalyst.Core.Lib.Cryptography; +using NUnit.Framework; + +namespace Catalyst.Core.Lib.Tests.UnitTests.Cryptography +{ + public class SecureStringExtensionsTest + { + [Test] + public void UseBytes() + { + var secret = new SecureString(); + var expected = new[] + { + 'a', 'b', 'c' + }; + + foreach (var c in expected) + { + secret.AppendChar(c); + } + + secret.UseSecretBytes(bytes => + { + Assert.AreEqual(expected.Length, bytes.Length); + for (var i = 0; i < expected.Length; ++i) + { + Assert.AreEqual(expected[i], (int) bytes[i]); + } + }); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/DAO/DaoTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/DAO/DaoTests.cs index d24e8b8e93..b1a08c919a 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/DAO/DaoTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/DAO/DaoTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,43 +22,47 @@ #endregion using System; -using System.Linq; using System.Net; using System.Text; using Catalyst.Abstractions.DAO; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Cryptography; using Catalyst.Core.Lib.DAO.Deltas; +using Catalyst.Core.Lib.DAO.Peer; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Deltas; using Catalyst.Protocol.Network; using Catalyst.Protocol.Peer; -using Catalyst.Protocol.Wire; using Catalyst.Protocol.Transaction; +using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using Catalyst.TestUtils.Protocol; using FluentAssertions; +using MultiFormats; +using MultiFormats.Registry; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; using Nethermind.Dirichlet.Numerics; -using TheDotNetLeague.MultiFormats.MultiBase; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using CandidateDeltaBroadcast = Catalyst.Protocol.Wire.CandidateDeltaBroadcast; +using NUnit.Framework; +using Catalyst.Core.Modules.Kvm; namespace Catalyst.Core.Lib.Tests.UnitTests.DAO { public class DaoTests { - private readonly IMapperInitializer[] _initialisers; private readonly HashProvider _hashProvider; - private MapperProvider _mapperProvider; + private readonly MapperProvider _mapperProvider; public DaoTests() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); - _initialisers = new IMapperInitializer[] + var initialisers = new IMapperInitializer[] { new ProtocolMessageMapperInitialiser(), new ConfidentialEntryMapperInitialiser(), @@ -71,45 +75,39 @@ public DaoTests() new DeltaDfsHashBroadcastMapperInitialiser(), new FavouriteDeltaBroadcastMapperInitialiser(), new CoinbaseEntryMapperInitialiser(), - new PublicEntryMapperInitialiser(), + new PublicEntryMapperInitialiser(_hashProvider), new ConfidentialEntryMapperInitialiser(), new TransactionBroadcastMapperInitialiser(), - new ContractEntryMapperInitialiser(), - new SignatureMapperInitialiser(), - new BaseEntryMapperInitialiser(), + new SignatureMapperInitialiser() }; - _mapperProvider = new MapperProvider(_initialisers); + _mapperProvider = new MapperProvider(initialisers); } - // ReSharper disable once UnusedMember.Local - private TDao GetMapper() where TDao : IMapperInitializer => _initialisers.OfType().First(); - - [Fact] + [Test] public void ProtocolMessageDao_ProtocolMessage_Should_Be_Convertible() { var newGuid = Guid.NewGuid(); - var peerId = PeerIdHelper.GetPeerId("testcontent"); + var address = MultiAddressHelper.GetAddress("testcontent"); var original = new ProtocolMessage { CorrelationId = newGuid.ToByteString(), TypeUrl = "cleanurl", Value = "somecontent".ToUtf8ByteString(), - PeerId = peerId + Address = address.ToString() }; var messageDao = original.ToDao(_mapperProvider); messageDao.TypeUrl.Should().Be("cleanurl"); messageDao.CorrelationId.Should().Be(newGuid.ToString()); - messageDao.PeerId.Port.Should().Be((ushort) peerId.Port); - messageDao.PeerId.Ip.Should().Be(new IPAddress(peerId.Ip.ToByteArray()).MapToIPv6().ToString()); + messageDao.Address.Should().Be(address.ToString()); var reconverted = messageDao.ToProtoBuff(_mapperProvider); reconverted.Should().Be(original); } - [Fact] + [Test] public void ProtocolErrorMessageSignedDao_ProtocolErrorMessageSigned_Should_Be_Convertible() { var byteRn = new byte[30]; @@ -123,26 +121,17 @@ public void ProtocolErrorMessageSignedDao_ProtocolErrorMessageSigned_Should_Be_C RawBytes = byteRn.ToByteString(), SigningContext = DevNetPeerSigningContext.Instance }, - PeerId = PeerIdHelper.GetPeerId("test"), + Address = MultiAddressHelper.GetAddress("test").ToString(), Code = 74 }; var errorMessageSignedDao = original.ToDao(_mapperProvider); - var reconverted = errorMessageSignedDao.ToProtoBuff(_mapperProvider); - reconverted.Should().Be(original); - } - - [Fact] - public void PeerIdDao_PeerId_Should_Be_Convertible() - { - var original = PeerIdHelper.GetPeerId("MyPeerId_Testing"); - - var peer = original.ToDao(_mapperProvider); - var reconverted = peer.ToProtoBuff(_mapperProvider); + var reconverted = + errorMessageSignedDao.ToProtoBuff(_mapperProvider); reconverted.Should().Be(original); } - [Fact] + [Test] public void SigningContextDao_SigningContext_Should_Be_Convertible() { var byteRn = new byte[30]; @@ -159,22 +148,7 @@ public void SigningContextDao_SigningContext_Should_Be_Convertible() reconverted.Should().Be(original); } - [Fact] - public void BaseEntryDao_And_BaseEntry_Should_Be_Convertible() - { - var original = new BaseEntry - { - ReceiverPublicKey = "hello".ToUtf8ByteString(), - SenderPublicKey = "bye bye".ToUtf8ByteString(), - TransactionFees = UInt256.MaxValue.ToUint256ByteString() - }; - - var contextDao = original.ToDao(_mapperProvider); - var reconverted = contextDao.ToProtoBuff(_mapperProvider); - reconverted.Should().Be(original); - } - - [Fact] + [Test] public void DeltasDao_Deltas_Should_Be_Convertible() { var previousHash = _hashProvider.ComputeMultiHash(Encoding.UTF8.GetBytes("previousHash")); @@ -186,7 +160,7 @@ public void DeltasDao_Deltas_Should_Be_Convertible() original.Should().Be(reconverted); } - [Fact] + [Test] public void CandidateDeltaBroadcastDao_CandidateDeltaBroadcast_Should_Be_Convertible() { var previousHash = _hashProvider.ComputeMultiHash(Encoding.UTF8.GetBytes("previousHash")); @@ -194,21 +168,25 @@ public void CandidateDeltaBroadcastDao_CandidateDeltaBroadcast_Should_Be_Convert var original = new CandidateDeltaBroadcast { - Hash = MultiBase.Decode(CidHelper.CreateCid(hash)).ToByteString(), - ProducerId = PeerIdHelper.GetPeerId("test"), - PreviousDeltaDfsHash = MultiBase.Decode(CidHelper.CreateCid(previousHash)).ToByteString() + Hash = MultiBase.Decode(hash.ToCid()).ToByteString(), + Producer = MultiAddressHelper.GetAddress("test").GetKvmAddressByteString(), + PreviousDeltaDfsHash = MultiBase.Decode(previousHash.ToCid()).ToByteString() }; - var candidateDeltaBroadcast = original.ToDao(_mapperProvider); - var reconverted = candidateDeltaBroadcast.ToProtoBuff(_mapperProvider); + var candidateDeltaBroadcast = + original.ToDao(_mapperProvider); + var reconverted = + candidateDeltaBroadcast.ToProtoBuff( + _mapperProvider); reconverted.Should().Be(original); } - [Fact] + [Test] public void DeltaDfsHashBroadcastDao_DeltaDfsHashBroadcast_Should_Be_Convertible() { - var hash = MultiBase.Decode(CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("this hash"))); - var previousDfsHash = MultiBase.Decode(CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("previousDfsHash"))); + var hash = MultiBase.Decode(_hashProvider.ComputeUtf8MultiHash("this hash").ToCid()); + var previousDfsHash = + MultiBase.Decode(_hashProvider.ComputeUtf8MultiHash("previousDfsHash").ToCid()); var original = new DeltaDfsHashBroadcast { @@ -221,21 +199,22 @@ public void DeltaDfsHashBroadcastDao_DeltaDfsHashBroadcast_Should_Be_Convertible reconverted.Should().Be(original); } - [Fact] + [Test] public void FavouriteDeltaBroadcastDao_FavouriteDeltaBroadcast_Should_Be_Convertible() { var original = new FavouriteDeltaBroadcast { - Candidate = DeltaHelper.GetCandidateDelta(_hashProvider, producerId: PeerIdHelper.GetPeerId("not me")), - VoterId = PeerIdHelper.GetPeerId("test") + Candidate = DeltaHelper.GetCandidateDelta(_hashProvider, producerId: MultiAddressHelper.GetAddress("not me").GetKvmAddress()), + Voter = MultiAddressHelper.GetAddress("test").GetKvmAddressByteString() }; var contextDao = original.ToDao(_mapperProvider); - var reconverted = contextDao.ToProtoBuff(_mapperProvider); + var reconverted = + contextDao.ToProtoBuff(_mapperProvider); reconverted.Should().Be(original); } - [Fact] + [Test] public void CoinbaseEntryDao_CoinbaseEntry_Should_Be_Convertible() { var pubKeyBytes = new byte[30]; @@ -243,18 +222,18 @@ public void CoinbaseEntryDao_CoinbaseEntry_Should_Be_Convertible() var original = new CoinbaseEntry { - ReceiverPublicKey = pubKeyBytes.ToByteString(), + ReceiverKvmAddress = pubKeyBytes.ToKvmAddressByteString(), Amount = 271314.ToUint256ByteString() }; var messageDao = original.ToDao(_mapperProvider); - messageDao.ReceiverPublicKey.Should().Be(pubKeyBytes.KeyToString()); + messageDao.ReceiverKvmAddress.Should().Be(pubKeyBytes.ToKvmAddress().ToString()); var reconverted = messageDao.ToProtoBuff(_mapperProvider); reconverted.Should().Be(original); } - [Fact] + [Test] public void STTransactionEntryDao_STTransactionEntry_Should_Be_Convertible() { var pubKeyBytes = new byte[30]; @@ -263,66 +242,92 @@ public void STTransactionEntryDao_STTransactionEntry_Should_Be_Convertible() var original = new PublicEntry { Amount = 8855274.ToUint256ByteString(), - Base = new BaseEntry + SenderAddress = pubKeyBytes.ToByteString(), + Signature = new Signature { - SenderPublicKey = pubKeyBytes.ToByteString(), - TransactionFees = UInt256.Zero.ToUint256ByteString() + RawBytes = new byte[] { 0x0 }.ToByteString(), + SigningContext = new SigningContext + { NetworkType = NetworkType.Devnet, SignatureType = SignatureType.TransactionPublic } } }; var transactionEntryDao = original.ToDao(_mapperProvider); - transactionEntryDao.Base.SenderPublicKey.Should().Be(pubKeyBytes.KeyToString()); - transactionEntryDao.Amount.Should().Be(8855274.ToString()); + transactionEntryDao.SenderAddress.Should().Be(pubKeyBytes.KeyToString()); + transactionEntryDao.Amount.Should().Be(8855274.ToUint256ByteString().ToByteArray().ToBase58()); var reconverted = transactionEntryDao.ToProtoBuff(_mapperProvider); - reconverted.Base.TransactionFees.ToUInt256().Should().Be(UInt256.Zero); reconverted.Should().Be(original); } - [Fact] + [Test] public void ConfidentialEntry_And_ConfidentialEntryDao_Should_Be_Convertible() { var pubKeyBytes = new byte[30]; var pedersenCommitBytes = new byte[50]; - var rangeProofBytes = new byte[50]; + var rangeProof = new RangeProof(); var rnd = new Random(); rnd.NextBytes(pubKeyBytes); rnd.NextBytes(pedersenCommitBytes); - rnd.NextBytes(rangeProofBytes); var original = new ConfidentialEntry { - Base = new BaseEntry - { - Nonce = ulong.MaxValue, - SenderPublicKey = pubKeyBytes.ToByteString(), - TransactionFees = UInt256.Zero.ToUint256ByteString() - }, + Nonce = ulong.MaxValue, + SenderPublicKey = pubKeyBytes.ToByteString(), + TransactionFees = UInt256.Zero.ToUint256ByteString(), PedersenCommitment = pedersenCommitBytes.ToByteString(), - RangeProof = rangeProofBytes.ToByteString() + RangeProof = rangeProof }; var transactionEntryDao = original.ToDao(_mapperProvider); - transactionEntryDao.Base.SenderPublicKey.Should().Be(pubKeyBytes.KeyToString()); - transactionEntryDao.Base.Nonce.Should().Be(ulong.MaxValue); + transactionEntryDao.SenderPublicKey.Should().Be(pubKeyBytes.KeyToString()); + transactionEntryDao.Nonce.Should().Be(ulong.MaxValue); transactionEntryDao.PedersenCommitment.Should().Be(pedersenCommitBytes.ToByteString().ToBase64()); - transactionEntryDao.RangeProof.Should().Be(rangeProofBytes.ToByteString().ToBase64()); + transactionEntryDao.RangeProof.Should().Be(rangeProof.ToByteString().ToBase64()); var reconverted = transactionEntryDao.ToProtoBuff(_mapperProvider); reconverted.Should().Be(original); } - [Fact] + [Test] public void TransactionBroadcastDao_TransactionBroadcast_Should_Be_Convertible() { var original = TransactionHelper.GetPublicTransaction(); var transactionEntryDao = original.ToDao(_mapperProvider); - var reconverted = transactionEntryDao.ToProtoBuff(_mapperProvider); + var reconverted = + transactionEntryDao.ToProtoBuff(_mapperProvider); reconverted.Should().Be(original); } + + [Test] + public void PublicEntryDao_Should_Be_The_Same_When_Converted() + { + var pubKeyBytes = new byte[30]; + new Random().NextBytes(pubKeyBytes); + var original = new PublicEntry + { + Amount = new byte[] { 222, 11, 107, 58, 118, 64, 0, 0 }.ToByteString(), + SenderAddress = pubKeyBytes.ToByteString(), + Signature = new Signature + { + RawBytes = new byte[] { 0x0 }.ToByteString(), + SigningContext = new SigningContext + { NetworkType = NetworkType.Devnet, SignatureType = SignatureType.TransactionPublic } + } + }; + + var transactionEntryDao1 = original.ToDao(_mapperProvider); + var hashId1 = transactionEntryDao1.Id; + + var reconverted = transactionEntryDao1.ToProtoBuff(_mapperProvider); + + var transactionEntryDao2 = reconverted.ToDao(_mapperProvider); + var hashId2 = transactionEntryDao2.Id; + + hashId1.Should().Be(hashId2); + } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Extensions/ProtobufExtensionsTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Extensions/ProtobufExtensionsTests.cs index c0d1ba1a48..f2d411fb5e 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Extensions/ProtobufExtensionsTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Extensions/ProtobufExtensionsTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,44 +32,44 @@ using Catalyst.TestUtils; using FluentAssertions; using Google.Protobuf; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Extensions { public class ProtobufExtensionsTests { - [Fact] + [Test] public static void ToAnySigned_should_happen_new_guid_to_request_if_not_specified() { //this ensures we won't get Guid.Empty and then a risk of mismatch; - var wrapped = new PingRequest().ToProtocolMessage(PeerIdHelper.GetPeerId("you")); + var wrapped = new PingRequest().ToProtocolMessage(MultiAddressHelper.GetAddress("you")); wrapped.CorrelationId.Should().NotBeEquivalentTo(Guid.Empty.ToByteString()); } - [Fact] + [Test] public static void ToAnySigned_should_set_the_wrapper_fields() { var guid = CorrelationId.GenerateCorrelationId(); - var peerId = PeerIdHelper.GetPeerId("blablabla"); + var peerId = MultiAddressHelper.GetAddress("blablabla"); var wrapped = new PeerId().ToProtocolMessage(peerId, guid); wrapped.CorrelationId.ToCorrelationId().Id.Should().Be(guid.Id); - wrapped.PeerId.Should().Be(peerId); + wrapped.Address.Should().Be(peerId.ToString()); wrapped.TypeUrl.Should().Be(PeerId.Descriptor.ShortenedFullName()); } - [Fact] + [Test] public static void ToProtocolMessage_When_Processing_Request_Should_Generate_New_CorrelationId_If_Not_Specified() { - var peerId = PeerIdHelper.GetPeerId("someone"); + var peerId = MultiAddressHelper.GetAddress("someone"); var request = new GetPeerCountRequest().ToProtocolMessage(peerId); request.CorrelationId.ToCorrelationId().Should().NotBe(default); } - [Fact] + [Test] public static void ToProtocolMessage_When_Processing_Response_Should_Fail_If_No_CorrelationId_Specified() { - var peerId = PeerIdHelper.GetPeerId("someone"); + var peerId = MultiAddressHelper.GetAddress("someone"); var response = new GetPeerCountResponse { PeerCount = 13 @@ -78,7 +78,7 @@ public static void ToProtocolMessage_When_Processing_Response_Should_Fail_If_No_ .Should().Throw(); } - [Fact] + [Test] public void ToCorrelationId_Should_Take_Care_Of_All_ByteStrings() { var tooLong = ByteUtil.GenerateRandomByteArray(43).ToByteString(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Extensions/UInt256ExtensionsTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Extensions/UInt256ExtensionsTests.cs index 71d4e89299..7f60520cd9 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Extensions/UInt256ExtensionsTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Extensions/UInt256ExtensionsTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,13 +24,14 @@ using Catalyst.Core.Lib.Extensions; using FluentAssertions; using Nethermind.Dirichlet.Numerics; -using Xunit; +using NUnit.Framework; +using System.Collections.Generic; namespace Catalyst.Core.Lib.Tests.UnitTests.Extensions { public class UInt256ExtensionsTests { - private sealed class UInt256ConversionTestData : TheoryData + private sealed class UInt256ConversionTestData : List { public UInt256ConversionTestData() { @@ -44,8 +45,7 @@ public UInt256ConversionTestData() } } - [Theory] - [ClassData(typeof(UInt256ConversionTestData))] + [TestCaseSource(typeof(UInt256ConversionTestData))] public void UInt256_ToByteString_And_Back_Should_Keep_Value_Intact(UInt256 bigInt) { var x = (UInt256) 234; diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/AddressedEnvelopeToIMessageEncoderTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/AddressedEnvelopeToIMessageEncoderTests.cs index ebba441874..64ca605550 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/AddressedEnvelopeToIMessageEncoderTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/AddressedEnvelopeToIMessageEncoderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,14 +22,14 @@ #endregion using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Codecs; -using Catalyst.Core.Lib.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Codecs; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; using Catalyst.Protocol.IPPN; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels.Embedded; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Codecs { @@ -42,12 +42,12 @@ public AddressedEnvelopeToIMessageEncoderTests() _testChannel = new EmbeddedChannel(new AddressedEnvelopeToIMessageEncoder()); } - [Fact] + [Test] public void Can_Encode_Signed_Message_Dto_To_Protocol_Message_Signed() { var messageDto = new SignedMessageDto( - new PingRequest().ToProtocolMessage(PeerIdHelper.GetPeerId("TestSender")), - PeerIdHelper.GetPeerId("Test")); + new PingRequest().ToProtocolMessage(MultiAddressHelper.GetAddress("TestSender")), + MultiAddressHelper.GetAddress("Test")); _testChannel.WriteOutbound(messageDto); var outboundMessages = _testChannel.OutboundMessages.ToArray(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/DatagramPacketDecoderTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/DatagramPacketDecoderTests.cs index 0419f54c9f..91f0423f19 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/DatagramPacketDecoderTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/DatagramPacketDecoderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -31,13 +31,13 @@ using DotNetty.Transport.Channels.Embedded; using DotNetty.Transport.Channels.Sockets; using Google.Protobuf; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Codecs { public sealed class DatagramPacketDecoderTests { - [Fact] + [Test] public void DatagramPacketDecoder_Can_Decode_IMessage_With_ProtobufDecoder_And_ProtocolMessageSignedParser() { var channel = new EmbeddedChannel( @@ -56,7 +56,7 @@ public void DatagramPacketDecoder_Can_Decode_IMessage_With_ProtobufDecoder_And_P Assert.True(channel.WriteInbound(datagramPacket)); var content = channel.ReadInbound(); - Assert.Equal(protocolMessageSigned, content); + Assert.AreEqual(protocolMessageSigned, content); Assert.False(channel.Finish()); } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/DatagramPacketEncoderTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/DatagramPacketEncoderTests.cs index 66bfa2a42d..5d04110a7a 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/DatagramPacketEncoderTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Codecs/DatagramPacketEncoderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,10 +22,8 @@ #endregion using System.Net; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Protocol.Wire; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; using DotNetty.Buffers; using DotNetty.Codecs; @@ -33,43 +31,47 @@ using DotNetty.Transport.Channels.Embedded; using DotNetty.Transport.Channels.Sockets; using Google.Protobuf; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Codecs { public sealed class DatagramPacketEncoderTests { - private readonly EmbeddedChannel _channel; - private readonly PeerId _recipientPid; - private readonly DatagramPacket _datagramPacket; - private readonly ProtocolMessage _protocolMessageSigned; - - public DatagramPacketEncoderTests() + private EmbeddedChannel _channel; + private MultiAddress _recipientPid; + private DatagramPacket _datagramPacket; + private ProtocolMessage _protocolMessageSigned; + + [SetUp] + public void Init() { _channel = new EmbeddedChannel( new DatagramPacketEncoder(new ProtobufEncoder()) ); - var senderPid = PeerIdHelper.GetPeerId("sender", + var senderPid = MultiAddressHelper.GetAddress("sender", IPAddress.Loopback, 10000 ); - - _recipientPid = PeerIdHelper.GetPeerId("sender", + + _recipientPid = MultiAddressHelper.GetAddress("sender", IPAddress.Loopback, 20000 ); _protocolMessageSigned = new PingRequest().ToSignedProtocolMessage(senderPid, (byte[]) default); - + _datagramPacket = new DatagramPacket( Unpooled.WrappedBuffer(_protocolMessageSigned.ToByteArray()), - senderPid.IpEndPoint, - _recipientPid.IpEndPoint + senderPid.GetIPEndPoint(), + _recipientPid.GetIPEndPoint() ); } - [Fact] + [Test] public void DatagramPacketEncoder_Can_Encode_IMessage_With_ProtobufEncoder() { Assert.True(_channel.WriteOutbound(new SignedMessageDto(_protocolMessageSigned, _recipientPid))); @@ -77,32 +79,32 @@ public void DatagramPacketEncoder_Can_Encode_IMessage_With_ProtobufEncoder() var datagramPacket = _channel.ReadOutbound(); Assert.NotNull(datagramPacket); - Assert.Equal(_datagramPacket.Content, datagramPacket.Content); - Assert.Equal(_datagramPacket.Sender, datagramPacket.Sender); - Assert.Equal(_datagramPacket.Recipient, datagramPacket.Recipient); + Assert.AreEqual(_datagramPacket.Content, datagramPacket.Content); + Assert.AreEqual(_datagramPacket.Sender, datagramPacket.Sender); + Assert.AreEqual(_datagramPacket.Recipient, datagramPacket.Recipient); datagramPacket.Release(); Assert.False(_channel.Finish()); } - [Fact] + [Test] public void DatagramPacketEncoder_Will_Not_Encode_UnmatchedMessageType() { Assert.True(_channel.WriteOutbound(_protocolMessageSigned)); - + var protocolMessageSigned = _channel.ReadOutbound(); Assert.NotNull(protocolMessageSigned); - Assert.Same(_protocolMessageSigned, protocolMessageSigned); + Assert.AreSame(_protocolMessageSigned, protocolMessageSigned); Assert.False(_channel.Finish()); } - - [Fact] + + [Test] public void DatagramPacketEncoder_Will_Not_Encode_UnmatchedType() { const string expected = "junk"; Assert.True(_channel.WriteOutbound(expected)); - + var content = _channel.ReadOutbound(); - Assert.Same(expected, content); + Assert.AreSame(expected, content); Assert.False(_channel.Finish()); } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/EventLoopGroupFactoryTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/EventLoopGroupFactoryTests.cs index 99e3a26c2d..bb6a7fbfd7 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/EventLoopGroupFactoryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/EventLoopGroupFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,11 +25,11 @@ using System.Linq; using System.Reflection; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Core.Lib.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.IO.EventLoop; using DotNetty.Transport.Channels; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO { @@ -55,7 +55,7 @@ public EventLoopGroupFactoryTests() }; } - [Fact] + [Test] public void Can_Spawn_Correct_Amount_Of_Udp_Server_Event_Loops() { _eventFactory = new UdpServerEventLoopGroupFactory(_eventLoopGroupFactoryConfiguration); @@ -63,7 +63,7 @@ public void Can_Spawn_Correct_Amount_Of_Udp_Server_Event_Loops() AssertEventLoopSize(_eventFactory.GetOrCreateSocketIoEventLoopGroup(), ExpectedDefaultEventLoopThreadCount); } - [Fact] + [Test] public void Can_Spawn_Correct_Amount_Of_Udp_Client_Event_Loops() { _eventFactory = new UdpClientEventLoopGroupFactory(_eventLoopGroupFactoryConfiguration); @@ -71,7 +71,7 @@ public void Can_Spawn_Correct_Amount_Of_Udp_Client_Event_Loops() AssertEventLoopSize(_eventFactory.GetOrCreateSocketIoEventLoopGroup(), ExpectedDefaultEventLoopThreadCount); } - [Fact] + [Test] public void Can_Spawn_Correct_Amount_Of_Tcp_Server_Event_Loops() { _eventFactory = new TcpServerEventLoopGroupFactory(_eventLoopGroupFactoryConfiguration); @@ -79,7 +79,7 @@ public void Can_Spawn_Correct_Amount_Of_Tcp_Server_Event_Loops() AssertEventLoopSize(_eventFactory.GetOrCreateSocketIoEventLoopGroup(), ExpectedDefaultEventLoopThreadCount); } - [Fact] + [Test] public void Can_Spawn_Correct_Amount_Of_Tcp_Client_Event_Loops() { _eventFactory = new TcpClientEventLoopGroupFactory(_eventLoopGroupFactoryConfiguration); @@ -87,7 +87,7 @@ public void Can_Spawn_Correct_Amount_Of_Tcp_Client_Event_Loops() AssertEventLoopSize(_eventFactory.GetOrCreateSocketIoEventLoopGroup(), ExpectedDefaultEventLoopThreadCount); } - [Fact] + [Test] public async Task Can_Dispose_All_Event_Loops() { _eventFactory = new TcpClientEventLoopGroupFactory(_eventLoopGroupFactoryConfiguration); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Events/TransactionReceivedEventTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Events/TransactionReceivedEventTests.cs index 71f46a58e6..204aebee8e 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Events/TransactionReceivedEventTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Events/TransactionReceivedEventTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,89 +21,91 @@ #endregion +using System; using System.Linq; using Catalyst.Abstractions.Mempool; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Abstractions.Validators; -using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Events; using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Protocol.Rpc.Node; +using Catalyst.Protocol.Transaction; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using FluentAssertions; using Google.Protobuf; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Abstractions.P2P; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Events { public sealed class TransactionReceivedEventTests { - private readonly IMempool _mempool; - private readonly ITransactionValidator _transactionValidator; - private readonly IBroadcastManager _broadcastManager; - private readonly TransactionReceivedEvent _transactionReceivedEvent; + private IMempool _mempool; + private ITransactionValidator _transactionValidator; + private IPeerClient _peerClient; + private TransactionReceivedEvent _transactionReceivedEvent; - public TransactionReceivedEventTests() + [SetUp] + public void Init() { var mapperProvider = new TestMapperProvider(); - - _mempool = Substitute.For>(); + _mempool = Substitute.For>(); _transactionValidator = Substitute.For(); - _broadcastManager = Substitute.For(); + _peerClient = Substitute.For(); _transactionReceivedEvent = new TransactionReceivedEvent(_transactionValidator, _mempool, - _broadcastManager, + _peerClient, mapperProvider, Substitute.For()); } - [Fact] + [Test] public void Can_Send_Error_To_Invalid_Transaction() { - _transactionValidator.ValidateTransaction(Arg.Any()) + _transactionValidator.ValidateTransaction(Arg.Any()) .Returns(false); - _transactionReceivedEvent.OnTransactionReceived(new TransactionBroadcast() - .ToProtocolMessage(PeerIdHelper.GetPeerId(), CorrelationId.GenerateCorrelationId())).Should() + _transactionReceivedEvent.OnTransactionReceived(new TransactionBroadcast {PublicEntry = new PublicEntry{SenderAddress = new byte[32].ToByteString()}} + .ToProtocolMessage(MultiAddressHelper.GetAddress(), CorrelationId.GenerateCorrelationId()), false).Should() .Be(ResponseCode.Error); - _broadcastManager.DidNotReceiveWithAnyArgs()?.BroadcastAsync(default); + _peerClient.DidNotReceiveWithAnyArgs()?.BroadcastAsync(default); } - [Fact] - public void Can_Send_Error_If_Mempool_Contains_Transaction() + [Test] + public void Can_Send_Exists_If_Mempool_Contains_Transaction() { var transaction = TransactionHelper.GetPublicTransaction(); - _transactionValidator.ValidateTransaction(Arg.Any()) + _transactionValidator.ValidateTransaction(Arg.Any()) .Returns(true); - _mempool.Repository.TryReadItem(Arg.Any()).Returns(true); + _mempool.Service.TryReadItem(Arg.Any()).Returns(true); _transactionReceivedEvent - .OnTransactionReceived(transaction.ToProtocolMessage(PeerIdHelper.GetPeerId(), - CorrelationId.GenerateCorrelationId())) - .Should().Be(ResponseCode.Error); - _broadcastManager.DidNotReceiveWithAnyArgs()?.BroadcastAsync(default); - _mempool.Repository.DidNotReceiveWithAnyArgs().CreateItem(default); + .OnTransactionReceived(transaction.ToProtocolMessage(MultiAddressHelper.GetAddress(), + CorrelationId.GenerateCorrelationId()), false) + .Should().Be(ResponseCode.Exists); + _peerClient.DidNotReceiveWithAnyArgs()?.BroadcastAsync(default); + _mempool.Service.DidNotReceiveWithAnyArgs().CreateItem(default); } - [Fact] + [Test] public void Can_Broadcast_And_Save_Valid_Transaction() { var transaction = TransactionHelper.GetPublicTransaction(); - _transactionValidator.ValidateTransaction(Arg.Any()) + _transactionValidator.ValidateTransaction(Arg.Any()) .Returns(true); _transactionReceivedEvent - .OnTransactionReceived(transaction.ToProtocolMessage(PeerIdHelper.GetPeerId(), - CorrelationId.GenerateCorrelationId())) + .OnTransactionReceived(transaction.ToProtocolMessage(MultiAddressHelper.GetAddress(), + CorrelationId.GenerateCorrelationId()), true) .Should().Be(ResponseCode.Successful); - _mempool.Repository.Received(1).CreateItem(Arg.Any()); - _broadcastManager.Received(1)?.BroadcastAsync(Arg.Is( + _mempool.Service.Received(1).CreateItem(Arg.Any()); + _peerClient.Received(1)?.BroadcastAsync(Arg.Is( broadcastedMessage => broadcastedMessage.Value.ToByteArray().SequenceEqual(transaction.ToByteArray()))); } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/CorrelatableHandlerUnitTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/CorrelatableHandlerUnitTests.cs index e6cb7bbe72..b37a5989ed 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/CorrelatableHandlerUnitTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/CorrelatableHandlerUnitTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,18 +23,18 @@ using System.Threading.Tasks; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Protocol.Wire; using Catalyst.Protocol.IPPN; using Catalyst.TestUtils; using DotNetty.Transport.Channels; using Google.Protobuf; using NSubstitute; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Handlers { @@ -49,12 +49,12 @@ public CorrelatableHandlerUnitTests() _fakeContext = Substitute.For(); } - [Fact] + [Test] public async Task Does_Process_IMessageDto_Types() { var protocolMessage = - new PingRequest().ToProtocolMessage(PeerIdHelper.GetPeerId("sender")); - var messageDto = new MessageDto(protocolMessage, PeerIdHelper.GetPeerId("recipient")); + new PingRequest().ToProtocolMessage(MultiAddressHelper.GetAddress("sender")); + var messageDto = new MessageDto(protocolMessage, MultiAddressHelper.GetAddress("recipient")); var correlatableHandler = new CorrelatableHandler(_fakeMessageCorrelationManager); @@ -68,7 +68,7 @@ public async Task Does_Process_IMessageDto_Types() await _fakeContext.ReceivedWithAnyArgs(1).WriteAsync(Arg.Any>()); } - [Fact] + [Test] public void Does_Not_Process_OtherTypes_Types() { var fakeRequestMessageDto = Substitute.For>(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ObservableServiceHandlerUnitTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ObservableServiceHandlerUnitTests.cs index 7e81dc286f..fe2d8fcfac 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ObservableServiceHandlerUnitTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ObservableServiceHandlerUnitTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,12 +22,12 @@ #endregion using System; -using Catalyst.Core.Lib.IO.Handlers; using DotNetty.Transport.Channels; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Handlers { @@ -45,10 +45,10 @@ public ObservableServiceHandlerUnitTests() private readonly ObservableServiceHandler _observableServiceHandler; - [Fact] + [Test] public void Dispose_Should_Dispose_ObservableServiceHandler() { _observableServiceHandler.Dispose(); } - [Fact] + [Test] public void MessageStream_Should_Call_OnError_On_ExceptionCaught() { var channelHandlerContext = Substitute.For(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/PeerIdValidationHandlerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/PeerIdValidationHandlerTests.cs index f1c8e48595..a1047309ee 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/PeerIdValidationHandlerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/PeerIdValidationHandlerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,46 +23,47 @@ using Catalyst.Abstractions.P2P; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; +using MultiFormats; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Handlers { public class PeerIdValidationHandlerTests { - private readonly IPeerIdValidator _peerIdValidator; - private readonly PeerIdValidationHandler _peerIdValidationHandler; - private readonly IChannelHandlerContext _fakeContext; - private readonly ProtocolMessage _message; + private IPeerIdValidator _peerIdValidator; + private PeerIdValidationHandler _peerIdValidationHandler; + private IChannelHandlerContext _fakeContext; + private ProtocolMessage _message; - public PeerIdValidationHandlerTests() + [SetUp] + public void Init() { - _fakeContext = Substitute.For(); + _fakeContext = Substitute.For(); _peerIdValidator = Substitute.For(); _peerIdValidationHandler = new PeerIdValidationHandler(_peerIdValidator); - _message = new PingRequest().ToProtocolMessage(PeerIdHelper.GetPeerId("Test")) - .ToProtocolMessage(PeerIdHelper.GetPeerId("Test")); + _message = new PingRequest().ToProtocolMessage(MultiAddressHelper.GetAddress("Test")) + .ToProtocolMessage(MultiAddressHelper.GetAddress("Test")); } - [Fact] + [Test] public void Can_Stop_Next_Pipeline_On_Invalid_Peer() { - _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(false); + _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(false); _peerIdValidationHandler.ChannelRead(_fakeContext, _message); _fakeContext.DidNotReceiveWithAnyArgs().FireChannelRead(Arg.Any()); } - [Fact] + [Test] public void Can_Continue_Next_Pipeline_On_Valid_Peer() { - _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); + _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); _peerIdValidationHandler.ChannelRead(_fakeContext, _message); _fakeContext.ReceivedWithAnyArgs(1).FireChannelRead(Arg.Any()); } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ProtocolMessageSignHandlerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ProtocolMessageSignHandlerTests.cs index 1276ddb455..ebb181e55f 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ProtocolMessageSignHandlerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ProtocolMessageSignHandlerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,20 +22,20 @@ #endregion using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.KeySigner; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.Protocol.Wire; using Catalyst.Protocol.IPPN; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using Catalyst.TestUtils.Protocol; using DotNetty.Transport.Channels; using NSubstitute; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Handlers { @@ -43,24 +43,24 @@ public sealed class ProtocolMessageSignHandlerTests { private readonly IChannelHandlerContext _fakeContext; private readonly IMessageDto _dto; - private readonly IKeySigner _keySigner; + private readonly FakeKeySigner _keySigner; private readonly ISignature _signature; public ProtocolMessageSignHandlerTests() { _fakeContext = Substitute.For(); - _keySigner = Substitute.For(); + _keySigner = Substitute.For(); _signature = Substitute.For(); _signature.SignatureBytes.Returns(ByteUtil.GenerateRandomByteArray(new FfiWrapper().SignatureLength)); _signature.PublicKeyBytes.Returns(ByteUtil.GenerateRandomByteArray(new FfiWrapper().PublicKeyLength)); - _dto = new MessageDto(new PingRequest().ToProtocolMessage(PeerIdHelper.GetPeerId("sender")), - PeerIdHelper.GetPeerId("recipient") + _dto = new MessageDto(new PingRequest().ToProtocolMessage(MultiAddressHelper.GetAddress("sender")), + MultiAddressHelper.GetAddress("recipient") ); } - [Fact] + [Test] public void CantSignMessage() { var protocolMessageSignHandler = new ProtocolMessageSignHandler(_keySigner, DevNetPeerSigningContext.Instance); @@ -71,7 +71,7 @@ public void CantSignMessage() _fakeContext.ReceivedWithAnyArgs()?.WriteAsync(new object()); } - [Fact] + [Test] public void CanWriteAsyncOnSigningMessage() { _keySigner.Sign(Arg.Any(), default).ReturnsForAnyArgs(_signature); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ProtocolMessageVerifyHandlerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ProtocolMessageVerifyHandlerTests.cs index 5dab81e471..906330bafe 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ProtocolMessageVerifyHandlerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Handlers/ProtocolMessageVerifyHandlerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,45 +22,46 @@ #endregion using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.KeySigner; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Cryptography.BulletProofs; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.IPPN; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using Catalyst.TestUtils.Protocol; using DotNetty.Transport.Channels; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Handlers { public sealed class ProtocolMessageVerifyHandlerTests { - private readonly IChannelHandlerContext _fakeContext; - private readonly ProtocolMessage _protocolMessageSigned; - private readonly IKeySigner _keySigner; - private readonly SigningContext _signingContext; + private IChannelHandlerContext _fakeContext; + private ProtocolMessage _protocolMessageSigned; + private FakeKeySigner _keySigner; + private SigningContext _signingContext; - public ProtocolMessageVerifyHandlerTests() + [SetUp] + public void Init() { _fakeContext = Substitute.For(); - _keySigner = Substitute.For(); + _keySigner = Substitute.For(); _signingContext = DevNetPeerSigningContext.Instance; var signatureBytes = ByteUtil.GenerateRandomByteArray(new FfiWrapper().SignatureLength); var publicKeyBytes = ByteUtil.GenerateRandomByteArray(new FfiWrapper().PublicKeyLength); - var peerId = PeerIdHelper.GetPeerId(publicKeyBytes); + var peerId = MultiAddressHelper.GetAddress(publicKeyBytes); _protocolMessageSigned = new PingRequest() .ToSignedProtocolMessage(peerId, signatureBytes, _signingContext) .ToSignedProtocolMessage(peerId, signatureBytes, _signingContext); } - [Fact] - private void CanFireNextPipelineOnValidSignature() + [Test] + public void CanFireNextPipelineOnValidSignature() { _keySigner.Verify(Arg.Any(), Arg.Any(), default) .ReturnsForAnyArgs(true); @@ -72,8 +73,8 @@ private void CanFireNextPipelineOnValidSignature() _fakeContext.ReceivedWithAnyArgs().FireChannelRead(_protocolMessageSigned).Received(1); } - [Fact] - private void CanFireNextPipelineOnInvalidSignature() + [Test] + public void CanFireNextPipelineOnInvalidSignature() { _keySigner.Verify(Arg.Any(), Arg.Any(), default) .ReturnsForAnyArgs(false); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Correlation/CorrelationIdTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Correlation/CorrelationIdTests.cs index 6ee3fcd84f..41494fe609 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Correlation/CorrelationIdTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Correlation/CorrelationIdTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,13 +24,13 @@ using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Core.Lib.Util; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Messaging.Correlation { public class CorrelationIdTests { - [Fact] + [Test] public void CorrelationId_Be_Equal_When_Ids_Are_Equal() { var baseBytes = ByteUtil.GenerateRandomByteArray(CorrelationId.GuidByteLength); @@ -43,7 +43,7 @@ public void CorrelationId_Be_Equal_When_Ids_Are_Equal() (id1 == id2).Should().BeTrue(); } - [Fact] + [Test] public void CorrelationId_Not_Be_Equal_When_Ids_Are_Not_Equal() { var baseBytes = ByteUtil.GenerateRandomByteArray(CorrelationId.GuidByteLength); @@ -59,7 +59,7 @@ public void CorrelationId_Not_Be_Equal_When_Ids_Are_Not_Equal() (id1 == id3).Should().BeFalse(); } - [Fact] + [Test] public void ToString_Should_Be_The_Same_As_Guid_ToString() { var baseBytes = ByteUtil.GenerateRandomByteArray(CorrelationId.GuidByteLength); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Correlation/MessageCorrelationManagerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Correlation/MessageCorrelationManagerTests.cs index b087ac51a0..a66694a327 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Correlation/MessageCorrelationManagerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Correlation/MessageCorrelationManagerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,6 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.Util; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Protocol.Wire; using Catalyst.Protocol.IPPN; @@ -41,21 +40,23 @@ using Microsoft.Extensions.Primitives; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Messaging.Correlation { public abstract class MessageCorrelationManagerTests where T : IMessageCorrelationManager { - protected readonly PeerId[] PeerIds; + protected readonly MultiAddress[] PeerIds; protected List> PendingRequests; protected T CorrelationManager; protected readonly ILogger SubbedLogger; protected readonly IChangeTokenProvider ChangeTokenProvider; protected readonly IMemoryCache Cache; - private readonly PeerId _senderPeerId; + private readonly MultiAddress _sender; protected readonly Dictionary CacheEntriesByRequest = new Dictionary(); @@ -68,12 +69,12 @@ protected MessageCorrelationManagerTests() ChangeTokenProvider.GetChangeToken().Returns(changeToken); Cache = Substitute.For(); - _senderPeerId = PeerIdHelper.GetPeerId("sender"); + _sender = MultiAddressHelper.GetAddress("sender"); PeerIds = new[] { - PeerIdHelper.GetPeerId("peer1"), - PeerIdHelper.GetPeerId("peer2"), - PeerIdHelper.GetPeerId("peer3") + MultiAddressHelper.GetAddress("peer1"), + MultiAddressHelper.GetAddress("peer2"), + MultiAddressHelper.GetAddress("peer3") }; } @@ -84,7 +85,7 @@ protected void PrepareCacheWithPendingRequests() { PendingRequests = PeerIds.Select((p, i) => new CorrelatableMessage { - Content = new T().ToProtocolMessage(_senderPeerId, CorrelationId.GenerateCorrelationId()), + Content = new T().ToProtocolMessage(_sender, CorrelationId.GenerateCorrelationId()), Recipient = p, SentAt = DateTimeOffset.MinValue.Add(TimeSpan.FromMilliseconds(100 * i)) }).ToList(); @@ -115,7 +116,7 @@ private void AddCreateEntryExpectation(object key) CacheEntriesByRequest.Add(correlationId, cacheEntry); } - [Fact] + [Test] public virtual void New_Entries_Should_Be_Added_With_Individual_Entry_Options() { PendingRequests.ForEach(p => AddCreateEntryExpectation(p.Content.CorrelationId)); @@ -185,7 +186,7 @@ protected void TryMatchResponseAsync_Should_Not_Match_Existing_Records_With_Non_ request.Should().BeFalse(); } - [Fact] + [Test] public void UncorrelatedMessage_Should_Not_Propagate_To_Next_Pipeline() { var correlationManager = Substitute.For(); @@ -200,12 +201,12 @@ public void UncorrelatedMessage_Should_Not_Propagate_To_Next_Pipeline() channelHandlerContext.DidNotReceive().FireChannelRead(nonCorrelatedMessage); } - [Fact] + [Test] public void TryMatchResponseAsync_Should_Not_Match_On_Wrong_Response_Type() { var matchingRequest = PendingRequests[2].Content; var matchingRequestWithWrongType = - new PeerNeighborsResponse().ToProtocolMessage(matchingRequest.PeerId, + new PeerNeighborsResponse().ToProtocolMessage(matchingRequest.Address, matchingRequest.CorrelationId.ToCorrelationId()); new Action(() => CorrelationManager.TryMatchResponse(matchingRequestWithWrongType)) diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Dto/MessageDtoTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Dto/MessageDtoTests.cs index c731430132..023985869a 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Dto/MessageDtoTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Messaging/Dto/MessageDtoTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,15 +21,15 @@ #endregion -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Protocol.Wire; using Catalyst.Protocol.IPPN; using Catalyst.TestUtils; using FluentAssertions; using Google.Protobuf; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Messaging.Dto { @@ -40,20 +40,20 @@ public sealed class MessageDtoTests public MessageDtoTests() { var pingRequest = new PingRequest(); - _messageDto = new MessageDto(pingRequest.ToProtocolMessage(PeerIdHelper.GetPeerId("Sender_Key")), - PeerIdHelper.GetPeerId("Recipient_Key") + _messageDto = new MessageDto(pingRequest.ToProtocolMessage(MultiAddressHelper.GetAddress("Sender_Key")), + MultiAddressHelper.GetAddress("Recipient_Key") ); } - [Fact] + [Test] public void CanInitMessageDtoCorrectly() { Assert.NotNull(_messageDto); _messageDto.Should().BeOfType(); _messageDto.Content.Should().NotBeNull().And.BeAssignableTo(typeof(IMessage)); - _messageDto.RecipientPeerIdentifier.Should().NotBeNull(); - _messageDto.SenderPeerIdentifier.Should().NotBeNull(); + _messageDto.Recipient.Should().NotBeNull(); + _messageDto.Sender.Should().NotBeNull(); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/BroadcastObserverBaseTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/BroadcastObserverBaseTests.cs index 0c8d666816..df708fd78e 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/BroadcastObserverBaseTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/BroadcastObserverBaseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,17 +25,16 @@ using System.Linq; using System.Threading; using Catalyst.Abstractions.Hashing; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.IO.Observers; using Catalyst.Core.Modules.Hashing; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using FluentAssertions; using Microsoft.Reactive.Testing; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Observers { @@ -48,7 +47,7 @@ private sealed class FailingBroadCastObserver : BroadcastObserverBase messageDto) + public override void HandleBroadcast(ProtocolMessage message) { var count = Interlocked.Increment(ref _counter); if (count % 2 == 0) @@ -62,10 +61,10 @@ public override void HandleBroadcast(IObserverDto messageDto) public BroadcastObserverBaseTests() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); } - [Fact] + [Test] public void OnNext_Should_Still_Get_Called_After_HandleBroadcast_Failure() { var testScheduler = new TestScheduler(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/MessageObserverBaseTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/MessageObserverBaseTests.cs index 284ffe483f..9505a5b52f 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/MessageObserverBaseTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/MessageObserverBaseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,20 +24,17 @@ using System; using System.Linq; using System.Reactive.Subjects; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Core.Lib.Util; using Catalyst.Protocol.Wire; using Catalyst.Protocol.IPPN; using Catalyst.Protocol.Rpc.Node; using Catalyst.TestUtils; -using DotNetty.Transport.Channels; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Observers { @@ -48,29 +45,28 @@ public VanillaMessageObserver(ILogger logger) : base(logger) { } public class ObservableBaseTests { - private readonly TestScheduler _testScheduler; - private readonly VanillaMessageObserver _handler; - private readonly IChannelHandlerContext _fakeContext; - private readonly ProtocolMessage[] _responseMessages; + private TestScheduler _testScheduler; + private VanillaMessageObserver _handler; + private ProtocolMessage[] _responseMessages; - public ObservableBaseTests() + [SetUp] + public void Init() { _testScheduler = new TestScheduler(); _handler = new VanillaMessageObserver(Substitute.For()); - _fakeContext = Substitute.For(); _responseMessages = Enumerable.Range(0, 10).Select(i => { - var message = new GetInfoResponse {Query = i.ToString()}; + var message = new GetInfoResponse { Query = i.ToString() }; return message.ToProtocolMessage( - PeerIdHelper.GetPeerId(i.ToString()), + MultiAddressHelper.GetAddress(i.ToString()), CorrelationId.GenerateCorrelationId()); }).ToArray(); } - [Fact] + [Test] public void MessageHandler_should_subscribe_to_next_and_complete() { - var completingStream = MessageStreamHelper.CreateStreamWithMessages(_fakeContext, _testScheduler, _responseMessages); + var completingStream = MessageStreamHelper.CreateStreamWithMessages(_testScheduler, _responseMessages); _handler.StartObserving(completingStream); @@ -81,11 +77,11 @@ public void MessageHandler_should_subscribe_to_next_and_complete() _handler.SubstituteObserver.Received(1).OnCompleted(); } - [Fact] + [Test] public void MessageHandler_should_subscribe_to_next_and_error() { - var erroringStream = new ReplaySubject>(10, _testScheduler); - + var erroringStream = new ReplaySubject(10, _testScheduler); + _handler.StartObserving(erroringStream); foreach (var payload in _responseMessages) @@ -95,7 +91,7 @@ public void MessageHandler_should_subscribe_to_next_and_error() erroringStream.OnError(new DataMisalignedException("5 erred")); } - erroringStream.OnNext(new ObserverDto(_fakeContext, payload)); + erroringStream.OnNext(payload); } _testScheduler.Start(); @@ -105,18 +101,18 @@ public void MessageHandler_should_subscribe_to_next_and_error() _handler.SubstituteObserver.Received(0).OnCompleted(); } - [Fact] + [Test] public void MessageHandler_should_not_receive_messages_of_the_wrong_type() { _responseMessages[3] = new PingResponse().ToProtocolMessage( - _responseMessages[3].PeerId, + _responseMessages[3].Address, _responseMessages[3].CorrelationId.ToCorrelationId()); _responseMessages[7] = new PingRequest().ToProtocolMessage( - _responseMessages[7].PeerId, + _responseMessages[7].Address, _responseMessages[7].CorrelationId.ToCorrelationId()); - var mixedTypesStream = MessageStreamHelper.CreateStreamWithMessages(_fakeContext, _testScheduler, _responseMessages); + var mixedTypesStream = MessageStreamHelper.CreateStreamWithMessages(_testScheduler, _responseMessages); _handler.StartObserving(mixedTypesStream); @@ -127,14 +123,14 @@ public void MessageHandler_should_not_receive_messages_of_the_wrong_type() _handler.SubstituteObserver.Received(1).OnCompleted(); } - [Fact] + [Test] public void MessageHandler_should_not_receive_null_or_untyped_messages() { _responseMessages[2].TypeUrl = ""; _responseMessages[5] = NullObjects.ProtocolMessage; _responseMessages[9] = null; - var mixedTypesStream = MessageStreamHelper.CreateStreamWithMessages(_fakeContext, _testScheduler, _responseMessages); + var mixedTypesStream = MessageStreamHelper.CreateStreamWithMessages(_testScheduler, _responseMessages); _handler.StartObserving(mixedTypesStream); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/RequestObserverBaseTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/RequestObserverBaseTests.cs index b21affc835..9bd280aed9 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/RequestObserverBaseTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/RequestObserverBaseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,21 +28,24 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Abstractions.P2P; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Observers { public sealed class RequestObserverBaseTests { - [Fact] + [Test] public void OnNext_Should_Still_Get_Called_After_HandleBroadcast_Failure() { - var testScheduler = new TestScheduler(); + var testScheduler = new TestScheduler(); + var peerClient = Substitute.For(); var candidateDeltaMessages = Enumerable.Repeat(new PeerNeighborsRequest(), 10).ToArray(); - var peerSettings = PeerIdHelper.GetPeerId("server").ToSubstitutedPeerSettings(); + var peerSettings = MultiAddressHelper.GetAddress("server").ToSubstitutedPeerSettings(); var messageStream = MessageStreamHelper.CreateStreamWithMessages(testScheduler, candidateDeltaMessages); using (var observer = new FailingRequestObserver(Substitute.For(), - peerSettings)) + peerSettings, + peerClient)) { observer.StartObserving(messageStream); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/ResponseObserverBaseTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/ResponseObserverBaseTests.cs index e79b788bc5..4e9520989e 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/ResponseObserverBaseTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/ResponseObserverBaseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -34,7 +34,8 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Observers { @@ -48,8 +49,7 @@ private sealed class FailingResponseObserver : ResponseObserverBase * @@ -26,13 +26,13 @@ using System.Threading.Tasks; using Catalyst.Core.Lib.IO.Transport.Bootstrapping; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Transport.Bootstrapping { public sealed class BootstrapUnitTests { - [Fact] + [Test] public async Task BindAsync_Should_Bind_To_NettyBootstrap_BindAsync() { var ipAddress = IPAddress.Loopback; @@ -41,8 +41,7 @@ public async Task BindAsync_Should_Bind_To_NettyBootstrap_BindAsync() var bootstrap = new Bootstrap(); //We have not set the group for bootstrap so we know that code will trigger an exception, if BindAsync calls Base BindAsync - var exception = await Record.ExceptionAsync(async () => { await bootstrap.BindAsync(ipAddress, port); }); - exception.Should().BeOfType("group not set"); + Assert.ThrowsAsync(async () => { await bootstrap.BindAsync(ipAddress, port); }); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Bootstrapping/ServerBootstrapUnitTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Bootstrapping/ServerBootstrapUnitTests.cs index ec59ebddf1..7eda31b4ae 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Bootstrapping/ServerBootstrapUnitTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Bootstrapping/ServerBootstrapUnitTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,13 +26,13 @@ using System.Threading.Tasks; using Catalyst.Core.Lib.IO.Transport.Bootstrapping; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Transport.Bootstrapping { public sealed class ServerBootstrapUnitTests { - [Fact] + [Test] public async Task BindAsync_Should_Bind_To_NettyServerBootstrap_BindAsync() { var ipAddress = IPAddress.Loopback; @@ -41,8 +41,7 @@ public async Task BindAsync_Should_Bind_To_NettyServerBootstrap_BindAsync() var serverBootstrap = new ServerBootstrap(); //We have not set the group for bootstrap so we know that code will trigger an exception, if BindAsync calls Base BindAsync - var exception = await Record.ExceptionAsync(async () => { await serverBootstrap.BindAsync(ipAddress, port); }); - exception.Should().BeOfType("group not set"); + Assert.ThrowsAsync(async () => { await serverBootstrap.BindAsync(ipAddress, port); }); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/ClientChannelInitializerBaseTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/ClientChannelInitializerBaseTests.cs index 0e150e0401..fe94f42210 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/ClientChannelInitializerBaseTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/ClientChannelInitializerBaseTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,12 +25,12 @@ using System.Collections.Generic; using System.Net; using System.Security.Cryptography.X509Certificates; -using Catalyst.Core.Lib.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; using DotNetty.Handlers.Tls; using DotNetty.Transport.Channels; using FluentAssertions; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Transport.Channels { @@ -45,7 +45,7 @@ public ClientChannelInitializerBaseTests() private readonly ClientChannelInitializerBase _clientChannelInitializerBase; - [Fact] + [Test] public void NewTlsHandler_With_Correct_Parameters_Should_Return_TlsHandler() { var tlsHandler = @@ -53,28 +53,28 @@ public void NewTlsHandler_With_Correct_Parameters_Should_Return_TlsHandler() tlsHandler.Should().BeOfType(); } - [Fact] + [Test] public void NewTlsHandler_With_Null_Parameters_Should_Return_Null() { var tlsHandler = _clientChannelInitializerBase.NewTlsHandler(null, null); tlsHandler.Should().BeNull(); } - [Fact] + [Test] public void NewTlsHandler_With_Null_TargetHost_Should_Return_Null() { var tlsHandler = _clientChannelInitializerBase.NewTlsHandler(null, Substitute.For()); tlsHandler.Should().BeNull(); } - [Fact] + [Test] public void NewTlsHandler_With_Null_X509Certificate_Should_Return_Null() { var tlsHandler = _clientChannelInitializerBase.NewTlsHandler(IPAddress.Any, null); tlsHandler.Should().BeNull(); } - [Fact] + [Test] public void When_ToString_Is_Called_Return_Custom_String() { const string targetValue = "OutboundInitializer[IChannel]"; diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/ObservableChannelTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/ObservableChannelTests.cs index 6e07bd3f72..03a51ecbc8 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/ObservableChannelTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/ObservableChannelTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,13 +25,12 @@ using System.Reactive.Linq; using System.Reactive.Subjects; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Core.Lib.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using FluentAssertions; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Transport.Channels { @@ -40,32 +39,31 @@ public sealed class ObservableChannelTests public ObservableChannelTests() { _channel = Substitute.For(); - var messageSubject = new ReplaySubject>(1); + var messageSubject = new ReplaySubject(1); _messageStream = messageSubject.AsObservable(); - _observableChannel = new ObservableChannel(_messageStream, _channel); + _observableChannel = new ObservableChannel(_messageStream, _channel); } private readonly IChannel _channel; - private readonly IObservable> _messageStream; - private readonly ObservableChannel _observableChannel; + private readonly IObservable _messageStream; + private readonly ObservableChannel _observableChannel; - [Fact] + [Test] public void MessageStream_Should_Not_Be_Null() { - var exception = Record.Exception(() => new ObservableChannel(null, _channel)); - exception.Should().BeOfType(); + Assert.Throws(() => new ObservableChannel(null, _channel)); } - [Fact] + [Test] public void Get_Channel_Should_Return_Correct_Channel() { _observableChannel.Channel.Should().Be(_channel); } - [Fact] + [Test] public void Get_MessageStream_Should_Return_Correct_MessageStream() { _observableChannel.MessageStream.Should().Be(_messageStream); } - [Fact] + [Test] public void StartAsync_Should_Return_Completed_Task() { var completedTask = _observableChannel.StartAsync(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/TcpClientChannelFactoryTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/TcpClientChannelFactoryTests.cs index bb1e7717fe..1c9f729e15 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/TcpClientChannelFactoryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/TcpClientChannelFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,19 +21,19 @@ #endregion -using System.Net; using System.Threading.Tasks; -using Catalyst.Core.Lib.IO.EventLoop; -using Catalyst.Core.Lib.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; +using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Transport.Channels { public sealed class TcpClientChannelFactoryTests { - [Fact] + [Test] public async Task TcpClientChannelFactory_BuildChannel_Should_Return_IObservableChannel() { var eventLoopGroupFactoryConfiguration = new EventLoopGroupFactoryConfiguration @@ -42,13 +42,12 @@ public async Task TcpClientChannelFactory_BuildChannel_Should_Return_IObservable }; var eventLoopGroupFactory = new TcpClientEventLoopGroupFactory(eventLoopGroupFactoryConfiguration); - var ipAddress = IPAddress.Loopback; - var port = 9000; + var address = "/ip4/127.0.0.1/tcp/9000"; var testTcpClientChannelFactory = new TestTcpClientChannelFactory(); - var channel = await testTcpClientChannelFactory.BuildChannelAsync(eventLoopGroupFactory, ipAddress, port).ConfigureAwait(false); + var channel = await testTcpClientChannelFactory.BuildChannelAsync(eventLoopGroupFactory, address).ConfigureAwait(false); - channel.Should().BeOfType(); + channel.Should().BeOfType>(); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/TcpServerChannelFactoryTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/TcpServerChannelFactoryTests.cs index 530cab05ed..6535c88442 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/TcpServerChannelFactoryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/Channels/TcpServerChannelFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,16 +21,17 @@ #endregion -using System.Net; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Core.Lib.IO.EventLoop; -using Catalyst.Core.Lib.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; +using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; using FluentAssertions; +using MultiFormats; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Transport.Channels { @@ -44,34 +45,32 @@ public TcpServerChannelFactoryTests() }; _eventLoopGroupFactory = new TcpServerEventLoopGroupFactory(eventLoopGroupFactoryConfiguration); - _ipAddress = IPAddress.Loopback; - _port = 9000; + _address = "/ip4/127.0.0.1/tcp/9000"; _testTcServerChannelFactory = new TestTcpServerChannelFactory(); } private readonly TestTcpServerChannelFactory _testTcServerChannelFactory; private readonly TcpServerEventLoopGroupFactory _eventLoopGroupFactory; - private readonly IPAddress _ipAddress; - private readonly int _port; + private readonly MultiAddress _address; - [Fact] + [Test] public async Task Bootstrap_Should_Return_Channel() { var certificate = Substitute.For(); var channel = - await _testTcServerChannelFactory.BootstrapAsync(_eventLoopGroupFactory, _ipAddress, _port, certificate); + await _testTcServerChannelFactory.BootstrapAsync(_eventLoopGroupFactory, _address, certificate); channel.Should().BeAssignableTo(); } - [Fact] + [Test] public async Task BuildChannel_Should_Return_IObservableChannel() { var observableChannel = - await _testTcServerChannelFactory.BuildChannelAsync(_eventLoopGroupFactory, _ipAddress, _port).ConfigureAwait(false); + await _testTcServerChannelFactory.BuildChannelAsync(_eventLoopGroupFactory, _address).ConfigureAwait(false); - observableChannel.Should().BeOfType(); + observableChannel.Should().BeOfType>(); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/ClientBaseUnitTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/ClientBaseUnitTests.cs index 73295046a4..e907d731a5 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/ClientBaseUnitTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/ClientBaseUnitTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,24 +21,24 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Transport { public sealed class ClientBaseUnitTests { - [Fact] + [Test] public void SendMessage_Should_Write_Message_To_Channel() { var messageDto = Substitute.For>(); - var channelFactory = Substitute.For(); + var channelFactory = Substitute.For>(); var logger = Substitute.For(); var eventLoopGroupFactory = Substitute.For(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/SocketBaseUnitTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/SocketBaseUnitTests.cs index 57c628f616..f74b7535a0 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/SocketBaseUnitTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/SocketBaseUnitTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,30 +22,32 @@ #endregion using System.Net.Sockets; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.TestUtils; using NSubstitute; using NSubstitute.ExceptionExtensions; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Protocol.Wire; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Transport { public sealed class SocketBaseUnitTests { - public SocketBaseUnitTests() + [SetUp] + public void Init() { _logger = Substitute.For(); - var channelFactory = Substitute.For(); + var channelFactory = Substitute.For>(); var eventLoopGroupFactory = Substitute.For(); _testSocketBase = new TestSocketBase(channelFactory, _logger, eventLoopGroupFactory); } - private readonly ILogger _logger; - private readonly TestSocketBase _testSocketBase; + private ILogger _logger; + private TestSocketBase _testSocketBase; - [Fact] + [Test] public void SocketBase_Should_Dispose() { _testSocketBase.Dispose(); @@ -53,7 +55,7 @@ public void SocketBase_Should_Dispose() _logger.Received(1).Debug($"Disposing{typeof(TestSocketBase).Name}"); } - [Fact] + [Test] public void SocketBase_Should_Log_On_Dispose_Exception() { var socketException = new SocketException(); @@ -63,7 +65,7 @@ public void SocketBase_Should_Log_On_Dispose_Exception() _logger.Received(1).Error(socketException, "Dispose failed to complete."); } - [Fact] + [Test] public void SocketBase_Should_Not_Dispose() { _testSocketBase.DisposeProxy(false); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/SocketClientRegistryTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/SocketClientRegistryTests.cs index 9902ef9c81..dfcdbc1bf6 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/SocketClientRegistryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/SocketClientRegistryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,15 +27,14 @@ using System.Linq; using System.Net; using System.Reactive.Linq; -using Catalyst.Abstractions.IO.Transport; -using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.Rpc; using Catalyst.Core.Lib.IO.Events; -using Catalyst.Core.Lib.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Rpc; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Transport { @@ -65,7 +64,7 @@ private ISocketClientRegistry ConstructPopulatedSocketRepository( return clientSocketRegistry; } - [Fact] + [Test] public void Can_Add_Multiple_Sockets_To_Registry() { var socketsByEndpointHashCode = ConstructSocketByEndpointSampleData(); @@ -77,7 +76,7 @@ public void Can_Add_Multiple_Sockets_To_Registry() .And.ContainValues(socketsByEndpointHashCode.Values); } - [Fact] + [Test] public void Can_Add_Multiple_Sockets_To_Registry_And_Get_One() { var socketsByEndpointHashCode = ConstructSocketByEndpointSampleData(); @@ -91,7 +90,7 @@ public void Can_Add_Multiple_Sockets_To_Registry_And_Get_One() .BeAssignableTo(); } - [Fact] + [Test] public void Can_Add_Multiple_Sockets_To_Registry_And_Remove_One() { var socketsByEndpointHashCode = ConstructSocketByEndpointSampleData(); @@ -110,7 +109,7 @@ public void Can_Add_Multiple_Sockets_To_Registry_And_Remove_One() .And.ContainValues(socketsByEndpointHashCode.Values); } - [Fact] + [Test] public void Can_Add_Socket_To_Registry_And_Get_Same_Client_From_HashCode() { var clientSocketRegistry = new SocketClientRegistry(); @@ -131,35 +130,28 @@ public void Can_Add_Socket_To_Registry_And_Get_Same_Client_From_HashCode() .BeAssignableTo(); } - [Fact] - public void Can_init_peer_client_registry() - { - var socketRegistry = new SocketClientRegistry(); - Assert.Equal(socketRegistry.GetRegistryType(), typeof(IPeerClient).Name); - } - - [Fact] + [Test] public void Can_init_rcp_client_registry() { var socketRegistry = new SocketClientRegistry(); - Assert.Equal(socketRegistry.GetRegistryType(), typeof(IRpcClient).Name); + Assert.AreEqual(socketRegistry.GetRegistryType(), typeof(IRpcClient).Name); } - [Fact] + [Test] public void Can_init_tcp_client_registry() { var socketRegistry = new SocketClientRegistry(); - Assert.Equal(socketRegistry.GetRegistryType(), typeof(ITcpClient).Name); + Assert.AreEqual(socketRegistry.GetRegistryType(), typeof(ITcpClient).Name); } - [Fact] + [Test] public void Can_init_udp_client_registry() { var socketRegistry = new SocketClientRegistry(); - Assert.Equal(socketRegistry.GetRegistryType(), typeof(IUdpClient).Name); + Assert.AreEqual(socketRegistry.GetRegistryType(), typeof(IUdpClient).Name); } - [Fact] + [Test] public void Can_Listen_To_Registry_Client_Added_Events() { var testScheduler = new TestScheduler(); @@ -182,7 +174,7 @@ public void Can_Listen_To_Registry_Client_Added_Events() connectionEvents.Should().NotBeEmpty().And.HaveCount(5); } - [Fact] + [Test] public void Can_Listen_To_Registry_Client_Removed_Events() { var testScheduler = new TestScheduler(); @@ -209,7 +201,7 @@ public void Can_Listen_To_Registry_Client_Removed_Events() connectionEvents.Should().BeEmpty(); } - [Fact] + [Test] public void Can_Remove_Socket_From_Registry() { var clientSocketRegistry = new SocketClientRegistry(); @@ -235,7 +227,7 @@ public void Can_Remove_Socket_From_Registry() .Should().BeEmpty(); } - [Fact] + [Test] public void Cannot_Add_Inactive_Client() { var clientSocketRegistry = new SocketClientRegistry(); @@ -247,7 +239,7 @@ public void Cannot_Add_Inactive_Client() .Throw(); } - [Fact] + [Test] public void Socket_Registry_Has_A_List() { new SocketClientRegistry().Registry diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/TcpClientUnitTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/TcpClientUnitTests.cs index 0723c8384e..0104a6bd98 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/TcpClientUnitTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/TcpClientUnitTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,13 +23,13 @@ using System; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.TestUtils; -using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Protocol.Wire; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Transport { @@ -37,21 +37,20 @@ public sealed class TcpClientUnitTests { public TcpClientUnitTests() { - _tcpClientChannelFactory = Substitute.For(); + _tcpClientChannelFactory = Substitute.For>(); _logger = Substitute.For(); _eventLoopGroupFactory = Substitute.For(); } - private readonly ITcpClientChannelFactory _tcpClientChannelFactory; + private readonly ITcpClientChannelFactory _tcpClientChannelFactory; private readonly ILogger _logger; private readonly ITcpClientEventLoopGroupFactory _eventLoopGroupFactory; - [Fact] + [Test] public async Task TcpClient_Should_Be_Derivable() { var tcpClient = new TestTcpClient(_tcpClientChannelFactory, _logger, _eventLoopGroupFactory); - var exception = await Record.ExceptionAsync(() => tcpClient.StartAsync()); - exception.Should().BeOfType(); + Assert.Throws(() => tcpClient.StartAsync()); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/TcpServerUnitTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/TcpServerUnitTests.cs index d6b28a5725..789f525222 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/TcpServerUnitTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Transport/TcpServerUnitTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,29 +21,31 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.TestUtils; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Protocol.Wire; namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Transport { public sealed class TcpServerUnitTests { - public TcpServerUnitTests() + [SetUp] + public void Init() { - _tcpServerChannelFactory = Substitute.For(); + _tcpServerChannelFactory = Substitute.For>(); _logger = Substitute.For(); _eventLoopGroupFactory = Substitute.For(); } - private readonly ITcpServerChannelFactory _tcpServerChannelFactory; - private readonly ILogger _logger; - private readonly IEventLoopGroupFactory _eventLoopGroupFactory; + private ITcpServerChannelFactory _tcpServerChannelFactory; + private ILogger _logger; + private IEventLoopGroupFactory _eventLoopGroupFactory; - [Fact] + [Test] public void TcpServer_Should_Dispose() { var tcpServer = new TestTcpServer(_tcpServerChannelFactory, _logger, _eventLoopGroupFactory); @@ -52,7 +54,7 @@ public void TcpServer_Should_Dispose() _logger.Received(1).Debug($"Disposing{typeof(TestTcpServer).Name}"); } - [Fact] + [Test] public void TcpServer_Should_Not_Dispose() { var tcpServer = new TestTcpServer(_tcpServerChannelFactory, _logger, _eventLoopGroupFactory); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Network/DnsUnitTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Network/DnsUnitTests.cs index 64d66c9a60..9ea6d39ece 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Network/DnsUnitTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Network/DnsUnitTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,7 +32,7 @@ using FluentAssertions; using NSubstitute; using NSubstitute.ExceptionExtensions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Network { @@ -47,7 +47,7 @@ public DnsUnitTests() private readonly IDns _dns; private readonly ILookupClient _lookupClient; - [Fact] + [Test] public async Task Dns_GetTxtRecords_from_list_should_return_IDnsQueryResponse_for_valid_list_of_strings_param() { var urlList = new List(); @@ -66,7 +66,7 @@ public async Task Dns_GetTxtRecords_from_list_should_return_IDnsQueryResponse_fo responses.Should().Contain(r => r.Answers[0].DomainName.Value.StartsWith(domain2)); } - [Fact] + [Test] public async Task Dns_GetTxtRecords_from_list_should_return_IDnsQueryResponse_for_valid_list_of_strings_param_even_when_one_lookup_is_null() { @@ -88,7 +88,7 @@ public async Task responses.Should().NotContainNulls(); } - [Fact] + [Test] public async Task Dns_GetTxtRecords_should_return_IDnsQueryResponse_from_lookup_client() { var value = "hey"; @@ -105,7 +105,7 @@ public async Task Dns_GetTxtRecords_should_return_IDnsQueryResponse_from_lookup_ ((TxtRecord) txtRecords.Answers.First()).Text.Should().BeEquivalentTo(value); } - [Fact] + [Test] public async Task Dns_GetTxtRecords_When_Lookup_Throws_should_return_null() { _lookupClient.QueryAsync(Arg.Any(), Arg.Any()) diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastCleanupHandlerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastCleanupHandlerTests.cs index cb829671ea..da31a6ef4f 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastCleanupHandlerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastCleanupHandlerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,15 +21,15 @@ #endregion -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels.Embedded; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Messaging.Broadcast { @@ -45,13 +45,13 @@ public BroadcastCleanupHandlerTests() _fakeChannel = new EmbeddedChannel(broadcastCleanupHandler); } - [Fact] + [Test] public void Can_Clean_Up_Broadcast() { var correlationId = CorrelationId.GenerateCorrelationId(); var fakeMessage = new TransactionBroadcast() - .ToProtocolMessage(PeerIdHelper.GetPeerId("Test"), correlationId); + .ToProtocolMessage(MultiAddressHelper.GetAddress("Test"), correlationId); _fakeChannel.WriteInbound(fakeMessage); _broadcastManager.Received(1).RemoveSignedBroadcastMessageData(correlationId); } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastHandlerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastHandlerTests.cs index ce78ff3be9..cd8373ec4b 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastHandlerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastHandlerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,35 +23,36 @@ using System.Threading.Tasks; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.KeySigner; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using Catalyst.TestUtils.Protocol; using DotNetty.Transport.Channels.Embedded; using Microsoft.Reactive.Testing; using NSubstitute; using NSubstitute.ReceivedExtensions; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Messaging.Broadcast { public sealed class BroadcastHandlerTests { - private readonly IBroadcastManager _fakeBroadcastManager; - private readonly BroadcastHandler _broadcastHandler; - private readonly IKeySigner _keySigner; - private readonly ProtocolMessage _broadcastMessageSigned; - private readonly SigningContext _signingContext; - - public BroadcastHandlerTests() + private IBroadcastManager _fakeBroadcastManager; + private BroadcastHandler _broadcastHandler; + private FakeKeySigner _keySigner; + private ProtocolMessage _broadcastMessageSigned; + private SigningContext _signingContext; + + [SetUp] + public void Init() { - _keySigner = Substitute.For(); + _keySigner = Substitute.For(); _keySigner.Verify(Arg.Any(), Arg.Any(), default).ReturnsForAnyArgs(true); _fakeBroadcastManager = Substitute.For(); _broadcastHandler = new BroadcastHandler(_fakeBroadcastManager); @@ -61,14 +62,14 @@ public BroadcastHandlerTests() _signingContext = DevNetPeerSigningContext.Instance; - var peerId = PeerIdHelper.GetPeerId("Test"); + var peerId = MultiAddressHelper.GetAddress("Test"); var innerMessage = new TransactionBroadcast(); _broadcastMessageSigned = innerMessage .ToSignedProtocolMessage(peerId, fakeSignature, _signingContext) .ToSignedProtocolMessage(peerId, fakeSignature, _signingContext); } - [Fact] + [Test] public async Task Broadcast_Handler_Can_Notify_Manager_On_Incoming_Broadcast() { EmbeddedChannel channel = new EmbeddedChannel( @@ -83,7 +84,7 @@ await _fakeBroadcastManager.Received(Quantity.Exactly(1)) .ReceiveAsync(Arg.Any()); } - [Fact] + [Test] public void Broadcast_Can_Execute_Proto_Handler() { var testScheduler = new TestScheduler(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastManagerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastManagerTests.cs index bc1ef31a6e..a74fc648a4 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastManagerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Broadcast/BroadcastManagerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,73 +22,76 @@ #endregion using System; -using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Core.Lib.P2P.IO.Messaging.Broadcast; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; -using Catalyst.Protocol.Peer; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using FluentAssertions; using Microsoft.Extensions.Caching.Memory; using NSubstitute; using Serilog; -using Xunit; +using SharpRepository.InMemoryRepository; +using NUnit.Framework; +using Catalyst.Core.Lib.P2P.Repository; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.P2P; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Messaging.Broadcast { public sealed class BroadcastManagerTests : IDisposable { - private readonly IPeerRepository _peers; - private readonly IMemoryCache _cache; - private readonly IKeySigner _keySigner; - private readonly PeerId _senderPeerId; - private readonly IPeerSettings _peerSettings; - - public BroadcastManagerTests() + private IPeerRepository _peers; + private IMemoryCache _cache; + private FakeKeySigner _keySigner; + private MultiAddress _sender; + private IPeerSettings _peerSettings; + + [SetUp] + public void Init() { - _senderPeerId = PeerIdHelper.GetPeerId("sender"); - _keySigner = Substitute.For(); + _sender = MultiAddressHelper.GetAddress("sender"); + _keySigner = Substitute.For(); var fakeSignature = Substitute.For(); _keySigner.Sign(Arg.Any(), default).ReturnsForAnyArgs(fakeSignature); _keySigner.CryptoContext.SignatureLength.Returns(64); - _peers = Substitute.For(); + _peers = new PeerRepository(new InMemoryRepository()); _cache = new MemoryCache(new MemoryCacheOptions()); - _peerSettings = _senderPeerId.ToSubstitutedPeerSettings(); + _peerSettings = _sender.ToSubstitutedPeerSettings(); } - [Fact] + [Test] public async Task Can_Increase_Broadcast_Count_When_Broadcast_Owner_Broadcasting() { - await TestBroadcast(100, _senderPeerId, + await TestBroadcast(100, _sender, BroadcastManager.BroadcastOwnerMaximumGossipPeersPerRound).ConfigureAwait(false); } - [Fact] + [Test] public async Task Can_Increase_Broadcast_Count_When_Broadcasting() { - await TestBroadcast(100, - PeerIdHelper.GetPeerId("AnotherBroadcaster"), + await TestBroadcast(100, + MultiAddressHelper.GetAddress("AnotherBroadcaster"), BroadcastManager.MaxGossipPeersPerRound).ConfigureAwait(false); } - [Fact] + [Test] public async Task Can_Broadcast_Message_When_Not_Enough_Peers_To_Gossip() { var peerCount = BroadcastManager.MaxGossipPeersPerRound - 1; - await TestBroadcast(peerCount, _senderPeerId, + await TestBroadcast(peerCount, _sender, peerCount).ConfigureAwait(false); } - private async Task TestBroadcast(int peerCount, PeerId broadcaster, int expectedBroadcastCount) + private async Task TestBroadcast(int peerCount, MultiAddress broadcaster, int expectedBroadcastCount) { PopulatePeers(peerCount); var correlationId = await BroadcastMessage(broadcaster) @@ -101,31 +104,30 @@ private async Task TestBroadcast(int peerCount, PeerId broadcaster, int expected } [Theory] - [InlineData(1)] - [InlineData(6)] - [InlineData(3)] + [TestCase(1)] + [TestCase(6)] + [TestCase(3)] public async Task Can_Increase_Received_Count_When_Broadcast_Message_Is_Received(int receivedCount) { PopulatePeers(100); - var peerId = PeerIdHelper.GetPeerId("1"); - var senderIdentifier = PeerIdHelper.GetPeerId("sender"); + var peerId = MultiAddressHelper.GetAddress("1"); IBroadcastManager broadcastMessageHandler = new BroadcastManager( _peers, _peerSettings, - _cache, - Substitute.For(), + _cache, + Substitute.For(), _keySigner, Substitute.For()); var messageDto = new MessageDto( - TransactionHelper.GetPublicTransaction().ToProtocolMessage(senderIdentifier), + TransactionHelper.GetPublicTransaction().ToProtocolMessage(_sender), peerId ); var gossipDto = messageDto.Content - .ToProtocolMessage(senderIdentifier, messageDto.CorrelationId); + .ToProtocolMessage(_sender, messageDto.CorrelationId); await broadcastMessageHandler.ReceiveAsync(gossipDto); @@ -141,15 +143,15 @@ public async Task Can_Increase_Received_Count_When_Broadcast_Message_Is_Received value.ReceivedCount.Should().Be(receivedCount + 1); } - private async Task BroadcastMessage(PeerId broadcaster) + private async Task BroadcastMessage(MultiAddress broadcaster) { var gossipMessageHandler = new - BroadcastManager( + BroadcastManager( _peers, _peerSettings, - _cache, - Substitute.For(), - _keySigner, + _cache, + Substitute.For(), + _keySigner, Substitute.For()); var innerMessage = TransactionHelper.GetPublicTransaction() @@ -161,18 +163,13 @@ private async Task BroadcastMessage(PeerId broadcaster) private void PopulatePeers(int count) { - var peerList = new List(); for (var i = 10; i < count + 10; i++) { - var peer = new Peer + _peers.Add(new Peer { - PeerId = PeerIdHelper.GetPeerId(i.ToString()) - }; - peerList.Add(peer); - _peers.Get(peer.DocumentId).Returns(peer); + Address = MultiAddressHelper.GetAddress(i.ToString()) + }); } - - _peers.AsQueryable().Returns(peerList.AsQueryable()); } public void Dispose() diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Correlation/PeerMessageCorrelationManagerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Correlation/PeerMessageCorrelationManagerTests.cs index b94e4af33d..d656fe52b6 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Correlation/PeerMessageCorrelationManagerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Correlation/PeerMessageCorrelationManagerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -36,17 +36,20 @@ using Catalyst.Protocol.Peer; using FluentAssertions; using Microsoft.Reactive.Testing; +using MultiFormats; +using Nethermind.Core; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Messaging.Correlation { public sealed class PeerMessageCorrelationManagerTests : MessageCorrelationManagerTests { - private readonly TestScheduler _testScheduler; - private readonly Dictionary _reputationByPeerIdentifier; + private TestScheduler _testScheduler; + private Dictionary _reputationByAddress; - public PeerMessageCorrelationManagerTests() + [SetUp] + public void Init() { _testScheduler = new TestScheduler(); @@ -59,37 +62,37 @@ public PeerMessageCorrelationManagerTests() _testScheduler ); - _reputationByPeerIdentifier = PeerIds.ToDictionary(p => p, p => 0); + _reputationByAddress = PeerIds.ToDictionary(p => p.GetKvmAddress(), p => 0); PrepareCacheWithPendingRequests(); CorrelationManager.ReputationEventStream.Subscribe(change => { - if (!_reputationByPeerIdentifier.ContainsKey(change.PeerId)) + if (!_reputationByAddress.ContainsKey(change.Address)) { return; } - - _reputationByPeerIdentifier[change.PeerId] += change.ReputationEvent.Amount; + + _reputationByAddress[change.Address] += change.ReputationEvent.Amount; }); } - [Fact] + [Test] public void TryMatchResponseAsync_Should_Match_Existing_Records_With_Matching_Correlation_Id() { TryMatchResponseAsync_Should_Match_Existing_Records_With_Matching_Correlation_Id(); } - [Fact] + [Test] public void TryMatchResponseAsync_Should_Not_Match_Existing_Records_With_Non_Matching_Correlation_Id() { TryMatchResponseAsync_Should_Not_Match_Existing_Records_With_Non_Matching_Correlation_Id(); } - [Fact] + [Test] public void TryMatchResponseAsync_when_matching_should_increase_reputation() { - var reputationBefore = _reputationByPeerIdentifier[PeerIds[1]]; + var reputationBefore = _reputationByAddress[PeerIds[1].GetKvmAddress()]; var responseMatchingIndex1 = new PingResponse().ToProtocolMessage( PeerIds[1], @@ -100,17 +103,17 @@ public void TryMatchResponseAsync_when_matching_should_increase_reputation() _testScheduler.Start(); - var reputationAfter = _reputationByPeerIdentifier[PeerIds[1]]; + var reputationAfter = _reputationByAddress[PeerIds[1].GetKvmAddress()]; reputationAfter.Should().BeGreaterThan(reputationBefore); - _reputationByPeerIdentifier.Where(r => !r.Key.Equals(PeerIds[1])) + _reputationByAddress.Where(r => !r.Key.Equals(PeerIds[1].GetKvmAddress())) .Select(r => r.Value).Should().AllBeEquivalentTo(0); } - [Fact] + [Test] public void UncorrelatedMessage_should_decrease_reputation() { - var reputationBefore = _reputationByPeerIdentifier[PeerIds[1]]; + var reputationBefore = _reputationByAddress[PeerIds[1].GetKvmAddress()]; var responseMatchingIndex1 = new PingResponse().ToProtocolMessage( PeerIds[1], CorrelationId.GenerateCorrelationId()); @@ -119,7 +122,7 @@ public void UncorrelatedMessage_should_decrease_reputation() _testScheduler.Start(); - var reputationAfter = _reputationByPeerIdentifier[PeerIds[1]]; + var reputationAfter = _reputationByAddress[PeerIds[1].GetKvmAddress()]; reputationAfter.Should().BeLessThan(reputationBefore); } @@ -133,7 +136,7 @@ protected override void CheckCacheEntriesCallback() _testScheduler.Start(); - observer.Received(1).OnNext(Arg.Is(c => c.PeerId.Equals(PendingRequests[0].Content.PeerId) + observer.Received(1).OnNext(Arg.Is(c => c.Address.Equals(new MultiAddress(PendingRequests[0].Content.Address).GetKvmAddress()) && c.ReputationEvent.Equals(ReputationEventType.NoResponseReceived))); } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Dto/PeerClientMessageDtoTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Dto/PeerClientMessageDtoTests.cs index e0db7e25fd..e0578583ac 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Dto/PeerClientMessageDtoTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Messaging/Dto/PeerClientMessageDtoTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,17 +29,17 @@ using Catalyst.TestUtils; using FluentAssertions; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Messaging.Dto { public sealed class PeerClientMessageDtoTests { - [Fact] + [Test] public void Can_Create_Dto_For_IPPN_Message() { var pingRequest = new PingRequest(); - var pid = PeerIdHelper.GetPeerId("sender"); + var pid = MultiAddressHelper.GetAddress("sender"); var dto = new PeerClientMessageDto(pingRequest, pid, Substitute.For() @@ -49,7 +49,7 @@ public void Can_Create_Dto_For_IPPN_Message() dto.Sender.Should().Be(pid); } - [Fact] + [Test] public void Throws_Exception_For_Non_IPPN_Message() { var rpcMessage = new VersionRequest(); @@ -58,7 +58,7 @@ public void Throws_Exception_For_Non_IPPN_Message() { // ReSharper disable once ObjectCreationAsStatement new PeerClientMessageDto(rpcMessage, - PeerIdHelper.GetPeerId("sender"), + MultiAddressHelper.GetAddress("sender"), Substitute.For() ); }); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/DeltaHeightRequestObserverTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/DeltaHeightRequestObserverTests.cs new file mode 100644 index 0000000000..175aa38e42 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/DeltaHeightRequestObserverTests.cs @@ -0,0 +1,93 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Core.Lib.P2P.IO.Observers; +using Catalyst.Core.Lib.Service; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.IPPN; +using Catalyst.TestUtils; +using Lib.P2P; +using Microsoft.Reactive.Testing; +using MultiFormats; +using NSubstitute; +using Serilog; +using NUnit.Framework; +using Catalyst.Abstractions.P2P; +using Catalyst.Core.Abstractions.Sync; + +namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Observers +{ + public sealed class DeltaHeightRequestObserverTests : IDisposable + { + private readonly TestScheduler _testScheduler; + private readonly ILogger _subbedLogger; + private readonly DeltaHeightRequestObserver _deltaHeightRequestObserver; + private readonly IPeerClient _peerClient; + + public DeltaHeightRequestObserverTests() + { + _testScheduler = new TestScheduler(); + _subbedLogger = Substitute.For(); + _peerClient = Substitute.For(); + var peerSettings = MultiAddressHelper.GetAddress("sender").ToSubstitutedPeerSettings(); + var syncState = new SyncState { IsSynchronized = true }; + _deltaHeightRequestObserver = new DeltaHeightRequestObserver(peerSettings, + Substitute.For(), new TestMapperProvider(), _peerClient, syncState, + _subbedLogger + ); + } + + [Test] + public async Task Can_Process_DeltaHeightRequest_Correctly() + { + var deltaHeightRequestMessage = new LatestDeltaHashRequest(); + var channeledAny = deltaHeightRequestMessage.ToProtocolMessage(MultiAddressHelper.GetAddress(), + CorrelationId.GenerateCorrelationId()); + var observableStream = new[] { channeledAny }.ToObservable(_testScheduler); + + _deltaHeightRequestObserver.StartObserving(observableStream); + + _testScheduler.Start(); + + var hash = MultiHash.ComputeHash(new byte[32]); + var cid = new Cid { Hash = hash }; + + var responder = MultiAddressHelper.GetAddress(); + var protocolMessage = new LatestDeltaHashResponse + { + DeltaIndex = new DeltaIndex { Cid = cid.ToArray().ToByteString(), Height = 100 } + }.ToProtocolMessage(responder, CorrelationId.GenerateCorrelationId()); + + await _peerClient.ReceivedWithAnyArgs(1).SendMessageAsync(protocolMessage, responder).ConfigureAwait(false); + + _subbedLogger.ReceivedWithAnyArgs(1); + } + + public void Dispose() { _deltaHeightRequestObserver?.Dispose(); } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/DeltaHeightResponseObserverTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/DeltaHeightResponseObserverTests.cs new file mode 100644 index 0000000000..79f5602b75 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/DeltaHeightResponseObserverTests.cs @@ -0,0 +1,75 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.P2P.IO.Messaging.Dto; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Core.Lib.P2P.IO.Observers; +using Catalyst.Protocol.IPPN; +using Catalyst.TestUtils; +using DotNetty.Transport.Channels; +using Microsoft.Reactive.Testing; +using NSubstitute; +using Serilog; +using NUnit.Framework; + +namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Observers +{ + public sealed class DeltaHeightResponseObserverTests : IDisposable + { + private readonly DeltaHeightResponseObserver _observer; + + public DeltaHeightResponseObserverTests() + { + _observer = new DeltaHeightResponseObserver(Substitute.For(), Substitute.For()); + } + + [Test] + public void Observer_Can_Process_DeltaHeightResponse_Correctly() + { + var testScheduler = new TestScheduler(); + + var deltaHeightResponse = new LatestDeltaHashResponse(); + var protocolMessage = + deltaHeightResponse.ToProtocolMessage(MultiAddressHelper.GetAddress("sender"), + CorrelationId.GenerateCorrelationId()); + + var deltaHeightResponseObserver = Substitute.For>(); + + var messageStream = MessageStreamHelper.CreateStreamWithMessage(testScheduler, protocolMessage); + + _observer.StartObserving(messageStream); + + using (_observer.MessageStream.Subscribe(deltaHeightResponseObserver.OnNext)) + { + testScheduler.Start(); + + deltaHeightResponseObserver.Received(1).OnNext(Arg.Any()); + } + } + + public void Dispose() { _observer?.Dispose(); } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/DeltaHistoryRequestObserverTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/DeltaHistoryRequestObserverTests.cs new file mode 100644 index 0000000000..878b1942d3 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/DeltaHistoryRequestObserverTests.cs @@ -0,0 +1,121 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Reactive.Linq; +using System.Threading.Tasks; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Core.Lib.P2P.IO.Observers; +using Catalyst.Core.Lib.Service; +using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Hashing; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.IPPN; +using Catalyst.TestUtils; +using Google.Protobuf; +using Microsoft.Reactive.Testing; +using MultiFormats.Registry; +using NSubstitute; +using Serilog; +using SharpRepository.InMemoryRepository; +using NUnit.Framework; +using Catalyst.Abstractions.P2P; + +namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Observers +{ + public sealed class DeltaHistoryRequestObserverTests : IDisposable + { + private readonly TestScheduler _testScheduler; + private readonly ILogger _subbedLogger; + private readonly DeltaHistoryRequestObserver _deltaHistoryRequestObserver; + private readonly IPeerClient _peerClient; + + public DeltaHistoryRequestObserverTests() + { + _testScheduler = new TestScheduler(); + _subbedLogger = Substitute.For(); + _peerClient = Substitute.For(); + + var peerSettings = MultiAddressHelper.GetAddress("sender").ToSubstitutedPeerSettings(); + var deltaIndexService = new DeltaIndexService(new InMemoryRepository()); + + _deltaHistoryRequestObserver = new DeltaHistoryRequestObserver(peerSettings, + deltaIndexService, + new TestMapperProvider(), + _peerClient, + _subbedLogger + ); + } + + [Test] + public async Task Can_Process_DeltaHeightRequest_Correctly() + { + var deltaHistoryRequestMessage = new DeltaHistoryRequest(); + + var channeledAny = deltaHistoryRequestMessage.ToProtocolMessage(MultiAddressHelper.GetAddress(), + CorrelationId.GenerateCorrelationId() + ); + + var observableStream = new[] { channeledAny }.ToObservable(_testScheduler); + + _deltaHistoryRequestObserver.StartObserving(observableStream); + _testScheduler.Start(); + + var response = new DeltaHistoryResponse(); + var hp = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + var lastDeltaHash = hp.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32)); + + for (uint x = 0; x < 10; x++) + { + var delta = new Delta + { + PreviousDeltaDfsHash = lastDeltaHash.Digest.ToByteString() + }; + + var index = new DeltaIndex + { + Height = 10, + Cid = delta.ToByteString() + }; + + response.DeltaIndex.Add(index); + lastDeltaHash = hp.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32)); + } + + var responder = MultiAddressHelper.GetAddress(); + var protocolMessageResponse = response.ToProtocolMessage(responder, CorrelationId.GenerateCorrelationId()); + + await _peerClient.ReceivedWithAnyArgs(1).SendMessageAsync(protocolMessageResponse, responder) + .ConfigureAwait(false); + + _subbedLogger.ReceivedWithAnyArgs(1); + } + + public void Dispose() + { + _deltaHistoryRequestObserver?.Dispose(); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/GetNeighbourRequestObserverTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/GetNeighbourRequestObserverTests.cs index 373c426a61..5677bfcb6e 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/GetNeighbourRequestObserverTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/GetNeighbourRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,19 +27,17 @@ using System.Threading.Tasks; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Core.Lib.P2P.IO.Observers; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; -using DotNetty.Transport.Channels; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using SharpRepository.Repository.Specifications; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Abstractions.P2P; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Observers { @@ -47,36 +45,38 @@ public sealed class GetNeighbourRequestObserverTests : IDisposable { private readonly TestScheduler _testScheduler; private readonly ILogger _subbedLogger; - private readonly PeerId _peerId; + private readonly MultiAddress _peerId; private readonly IPeerRepository _subbedPeerRepository; + private readonly IPeerClient _peerClient; public GetNeighbourRequestObserverTests() { _testScheduler = new TestScheduler(); _subbedLogger = Substitute.For(); _subbedPeerRepository = Substitute.For(); - _peerId = PeerIdHelper.GetPeerId("testPeer"); + _peerId = MultiAddressHelper.GetAddress("testPeer"); + _peerClient = Substitute.For(); } - + private static void AddMockPeerToDbAndSetReturnExpectation(IReadOnlyList peer, - IPeerRepository store) + IPeerRepository repository) { - store.Add(peer); - store.FindAll(Arg.Any>()).Returns(peer); + repository.Add(peer); + repository.GetActivePeers(Arg.Any()).Returns(peer); } - [Fact] + [Test] public async Task Can_Process_GetNeighbourRequest_Correctly() { // mock a random set of peers var randomPeers = new List { - new Peer {PeerId = PeerIdHelper.GetPeerId("peer1"), LastSeen = DateTime.Now}, - new Peer {PeerId = PeerIdHelper.GetPeerId("peer2"), LastSeen = DateTime.Now}, - new Peer {PeerId = PeerIdHelper.GetPeerId("peer3"), LastSeen = DateTime.Now}, - new Peer {PeerId = PeerIdHelper.GetPeerId("peer4"), LastSeen = DateTime.Now}, - new Peer {PeerId = PeerIdHelper.GetPeerId("peer5"), LastSeen = DateTime.Now}, - new Peer {PeerId = PeerIdHelper.GetPeerId("peer6")} + new Peer {Address = MultiAddressHelper.GetAddress("peer1"), LastSeen = DateTime.Now}, + new Peer {Address = MultiAddressHelper.GetAddress("peer2"), LastSeen = DateTime.Now}, + new Peer {Address = MultiAddressHelper.GetAddress("peer3"), LastSeen = DateTime.Now}, + new Peer {Address = MultiAddressHelper.GetAddress("peer4"), LastSeen = DateTime.Now}, + new Peer {Address = MultiAddressHelper.GetAddress("peer5"), LastSeen = DateTime.Now}, + new Peer {Address = MultiAddressHelper.GetAddress("peer6")} }; // add them to the mocked repository, and set return expectation @@ -85,33 +85,31 @@ public async Task Can_Process_GetNeighbourRequest_Correctly() var peerSettings = _peerId.ToSubstitutedPeerSettings(); var neighbourRequestHandler = new GetNeighbourRequestObserver(peerSettings, _subbedPeerRepository, + _peerClient, _subbedLogger ); - + var peerNeighbourRequestMessage = new PeerNeighborsRequest(); - - var fakeContext = Substitute.For(); - var channeledAny = new ObserverDto(fakeContext, peerNeighbourRequestMessage.ToProtocolMessage(PeerIdHelper.GetPeerId(), CorrelationId.GenerateCorrelationId())); + + var channeledAny = peerNeighbourRequestMessage.ToProtocolMessage(MultiAddressHelper.GetAddress(), + CorrelationId.GenerateCorrelationId()); var observableStream = new[] {channeledAny}.ToObservable(_testScheduler); - + neighbourRequestHandler.StartObserving(observableStream); - + var peerNeighborsResponseMessage = new PeerNeighborsResponse(); - + for (var i = 0; i < 5; i++) { - peerNeighborsResponseMessage.Peers.Add(PeerIdHelper.GetPeerId()); + peerNeighborsResponseMessage.Peers.Add(MultiAddressHelper.GetAddress().ToString()); } _testScheduler.Start(); - await fakeContext.Channel.ReceivedWithAnyArgs(1) - .WriteAndFlushAsync(peerNeighborsResponseMessage.ToProtocolMessage(_peerId, CorrelationId.GenerateCorrelationId())); + await _peerClient.ReceivedWithAnyArgs(1).SendMessageAsync(peerNeighborsResponseMessage.ToProtocolMessage(_peerId, CorrelationId.GenerateCorrelationId()), + _peerId).ConfigureAwait(false); } - public void Dispose() - { - _subbedPeerRepository?.Dispose(); - } + public void Dispose() { _subbedPeerRepository?.Dispose(); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/GetNeighbourResponseObserverTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/GetNeighbourResponseObserverTests.cs index be6438d510..fba3da6bc8 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/GetNeighbourResponseObserverTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/GetNeighbourResponseObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -34,48 +34,47 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using System.Linq; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Observers { public sealed class GetNeighbourResponseObserverTests : IDisposable { - private readonly IChannelHandlerContext _fakeContext; private readonly GetNeighbourResponseObserver _observer; public GetNeighbourResponseObserverTests() { - _fakeContext = Substitute.For(); _observer = new GetNeighbourResponseObserver(Substitute.For()); } - [Fact] + [Test] public void Observer_Can_Process_GetNeighbourResponse_Correctly() { var testScheduler = new TestScheduler(); var peers = new[] { - PeerIdHelper.GetPeerId(), - PeerIdHelper.GetPeerId(), - PeerIdHelper.GetPeerId() + MultiAddressHelper.GetAddress(), + MultiAddressHelper.GetAddress(), + MultiAddressHelper.GetAddress() }; var peerNeighborsResponse = new PeerNeighborsResponse { Peers = { - peers + peers.Select(x=>x.ToString()) } }; var protocolMessage = - peerNeighborsResponse.ToProtocolMessage(PeerIdHelper.GetPeerId("sender"), + peerNeighborsResponse.ToProtocolMessage(MultiAddressHelper.GetAddress("sender"), CorrelationId.GenerateCorrelationId()); var peerNeighborsResponseObserver = Substitute.For>(); - var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, - protocolMessage); + var messageStream = MessageStreamHelper.CreateStreamWithMessage(testScheduler, protocolMessage); _observer.StartObserving(messageStream); @@ -92,10 +91,10 @@ public void Observer_Can_Process_GetNeighbourResponse_Correctly() } } - private bool test(IMessage msg, PeerId peerId) + private bool test(IMessage msg, MultiAddress address) { var x = (PeerNeighborsResponse) msg; - return x.Peers.Contains(peerId); + return x.Peers.Contains(address.ToString()); } public void Dispose() { _observer?.Dispose(); } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/PingRequestObserverTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/PingRequestObserverTests.cs index 3feb07bb2a..b37ce0175e 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/PingRequestObserverTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/PingRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,15 +26,15 @@ using System.Threading.Tasks; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Core.Lib.P2P.IO.Observers; using Catalyst.Protocol.IPPN; using Catalyst.TestUtils; -using DotNetty.Transport.Channels; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Abstractions.P2P; +using MultiFormats; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Observers { @@ -43,33 +43,33 @@ public sealed class PingRequestObserverTests : IDisposable private readonly TestScheduler _testScheduler; private readonly ILogger _subbedLogger; private readonly PingRequestObserver _pingRequestObserver; + private readonly IPeerClient _peerClient; public PingRequestObserverTests() { + _peerClient = Substitute.For(); _testScheduler = new TestScheduler(); _subbedLogger = Substitute.For(); - var peerSettings = PeerIdHelper.GetPeerId("sender").ToSubstitutedPeerSettings(); - _pingRequestObserver = new PingRequestObserver(peerSettings, - _subbedLogger - ); + var peerSettings = MultiAddressHelper.GetAddress("sender").ToSubstitutedPeerSettings(); + _pingRequestObserver = new PingRequestObserver(peerSettings, _peerClient, _subbedLogger); } - [Fact] + [Test] public async Task Can_Process_PingRequest_Correctly() { var pingRequestMessage = new PingRequest(); - var fakeContext = Substitute.For(); - var channeledAny = new ObserverDto(fakeContext, pingRequestMessage.ToProtocolMessage(PeerIdHelper.GetPeerId(), CorrelationId.GenerateCorrelationId())); + var channeledAny = pingRequestMessage.ToProtocolMessage(MultiAddressHelper.GetAddress(), CorrelationId.GenerateCorrelationId()); var observableStream = new[] {channeledAny}.ToObservable(_testScheduler); _pingRequestObserver.StartObserving(observableStream); _testScheduler.Start(); - await fakeContext.Channel.ReceivedWithAnyArgs(1) - .WriteAndFlushAsync(new PingResponse().ToProtocolMessage(PeerIdHelper.GetPeerId(), CorrelationId.GenerateCorrelationId())); - + var response = new PingResponse().ToProtocolMessage(MultiAddressHelper.GetAddress(), CorrelationId.GenerateCorrelationId()); + + await _peerClient.ReceivedWithAnyArgs(1).SendMessageAsync(response, Arg.Any()).ConfigureAwait(false); + _subbedLogger.ReceivedWithAnyArgs(1); } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/PingResponseObserverTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/PingResponseObserverTests.cs index d613f8e08a..a67730c320 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/PingResponseObserverTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/PingResponseObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,46 +22,42 @@ #endregion using System; -using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.P2P.IO.Messaging.Dto; +using Catalyst.Abstractions.P2P.Protocols; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Core.Lib.P2P.IO.Observers; using Catalyst.Protocol.IPPN; using Catalyst.TestUtils; -using DotNetty.Transport.Channels; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Observers { public sealed class PingResponseObserverTests : IDisposable { - private readonly IChannelHandlerContext _fakeContext; private readonly PingResponseObserver _observer; public PingResponseObserverTests() { - _fakeContext = Substitute.For(); - _observer = new PingResponseObserver(Substitute.For(), Substitute.For()); + _observer = new PingResponseObserver(Substitute.For(), Substitute.For()); } - [Fact] + [Test] public void Observer_Can_Process_PingResponse_Correctly() { var testScheduler = new TestScheduler(); var pingResponse = new PingResponse(); var protocolMessage = - pingResponse.ToProtocolMessage(PeerIdHelper.GetPeerId("sender"), + pingResponse.ToProtocolMessage(MultiAddressHelper.GetAddress("sender"), CorrelationId.GenerateCorrelationId()); var pingResponseObserver = Substitute.For>(); - var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, - protocolMessage); + var messageStream = MessageStreamHelper.CreateStreamWithMessage(testScheduler, protocolMessage); _observer.StartObserving(messageStream); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Transport/Channels/PeerClientChannelFactoryTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Transport/Channels/PeerClientChannelFactoryTests.cs index 8f81b91ff0..9f154f4bbb 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Transport/Channels/PeerClientChannelFactoryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Transport/Channels/PeerClientChannelFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,18 +28,15 @@ using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.P2P.IO.Transport.Channels; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.Protocol.IPPN; using Catalyst.Protocol.Network; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Embedded; using DotNetty.Transport.Channels.Sockets; @@ -47,7 +44,12 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.P2P.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Protocol.Wire; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Transport.Channels { @@ -81,13 +83,13 @@ public PeerClientChannelFactoryTests() _testScheduler = new TestScheduler(); _correlationManager = Substitute.For(); _gossipManager = Substitute.For(); - _keySigner = Substitute.For(); + _keySigner = Substitute.For(); var peerSettings = Substitute.For(); peerSettings.NetworkType.Returns(NetworkType.Devnet); var peerValidator = Substitute.For(); - peerValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); + peerValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); _factory = new TestPeerClientChannelFactory( _keySigner, @@ -97,7 +99,7 @@ public PeerClientChannelFactoryTests() _testScheduler); } - [Fact] + [Test] public void PeerClientChannelFactory_should_have_correct_handlers() { _factory.InheritedHandlers.Count(h => h != null).Should().Be(6); @@ -116,7 +118,7 @@ public async Task PeerClientChannelFactory_should_put_the_correct_handlers_on_th var testingChannel = new EmbeddedChannel("test".ToChannelId(), true, _factory.InheritedHandlers.ToArray()); - var senderId = PeerIdHelper.GetPeerId("sender"); + var senderId = MultiAddressHelper.GetAddress("sender"); var correlationId = CorrelationId.GenerateCorrelationId(); var protocolMessage = new PingRequest().ToProtocolMessage(senderId, correlationId); var signature = ByteUtil.GenerateRandomByteArray(new FfiWrapper().SignatureLength); @@ -127,7 +129,7 @@ public async Task PeerClientChannelFactory_should_put_the_correct_handlers_on_th default) .ReturnsForAnyArgs(true); - var observer = new ProtocolMessageObserver(0, Substitute.For()); + var observer = new ProtocolMessageObserver(0, Substitute.For()); var messageStream = ((ObservableServiceHandler) _factory.InheritedHandlers.Last()).MessageStream; @@ -141,7 +143,7 @@ public async Task PeerClientChannelFactory_should_put_the_correct_handlers_on_th _testScheduler.Start(); observer.Received.Count.Should().Be(1); - observer.Received.Single().Payload.CorrelationId.ToCorrelationId().Id.Should().Be(correlationId.Id); + observer.Received.Single().CorrelationId.ToCorrelationId().Id.Should().Be(correlationId.Id); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Transport/Channels/PeerServerChannelFactoryTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Transport/Channels/PeerServerChannelFactoryTests.cs index 1af185aca7..0e78908a77 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Transport/Channels/PeerServerChannelFactoryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Transport/Channels/PeerServerChannelFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,26 +28,27 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.P2P.IO.Transport.Channels; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.Protocol.Wire; using Catalyst.Protocol.IPPN; using Catalyst.Protocol.Network; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Embedded; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.P2P.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Transport.Channels { @@ -72,26 +73,30 @@ public TestPeerServerChannelFactory(IPeerMessageCorrelationManager correlationMa public IReadOnlyCollection InheritedHandlers => _handlers; } - private readonly TestScheduler _testScheduler; - private readonly IPeerMessageCorrelationManager _correlationManager; - private readonly IBroadcastManager _gossipManager; - private readonly IKeySigner _keySigner; - private readonly TestPeerServerChannelFactory _factory; - private readonly PeerId _senderId; - private readonly ICorrelationId _correlationId; - private readonly byte[] _signature; - - public PeerServerChannelFactoryTests() + private TestScheduler _testScheduler; + private IPeerClient _peerClient; + private IPeerMessageCorrelationManager _correlationManager; + private IBroadcastManager _gossipManager; + private FakeKeySigner _keySigner; + private TestPeerServerChannelFactory _factory; + private MultiAddress _senderId; + private ICorrelationId _correlationId; + private byte[] _signature; + + [SetUp] + public void Init() { _testScheduler = new TestScheduler(); + _peerClient = Substitute.For(); _correlationManager = Substitute.For(); _gossipManager = Substitute.For(); - _keySigner = Substitute.For(); + _keySigner = Substitute.For(); + var peerSettings = Substitute.For(); peerSettings.NetworkType.Returns(NetworkType.Devnet); var peerValidator = Substitute.For(); - peerValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); + peerValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); _factory = new TestPeerServerChannelFactory( _correlationManager, @@ -100,14 +105,14 @@ public PeerServerChannelFactoryTests() peerValidator, peerSettings, _testScheduler); - _senderId = PeerIdHelper.GetPeerId("sender"); + _senderId = MultiAddressHelper.GetAddress("sender"); _correlationId = CorrelationId.GenerateCorrelationId(); _signature = ByteUtil.GenerateRandomByteArray(new FfiWrapper().SignatureLength); _keySigner.Verify(Arg.Any(), Arg.Any(), default) .ReturnsForAnyArgs(true); } - [Fact] + [Test] public void PeerServerChannelFactory_should_have_correct_handlers() { _factory.InheritedHandlers.Count(h => h != null).Should().Be(7); @@ -121,7 +126,7 @@ public void PeerServerChannelFactory_should_have_correct_handlers() handlers[6].Should().BeOfType(); } - [Fact] + [Test] public async Task PeerServerChannelFactory_should_put_the_correct_handlers_on_the_inbound_pipeline() { var testingChannel = new EmbeddedChannel("test".ToChannelId(), @@ -131,7 +136,7 @@ public async Task PeerServerChannelFactory_should_put_the_correct_handlers_on_th var signedMessage = protocolMessage.ToSignedProtocolMessage(signature: _signature); - var observer = new ProtocolMessageObserver(0, Substitute.For()); + var observer = new ProtocolMessageObserver(0, Substitute.For()); var messageStream = GetObservableServiceHandler().MessageStream; @@ -146,19 +151,19 @@ public async Task PeerServerChannelFactory_should_put_the_correct_handlers_on_th _testScheduler.Start(); observer.Received.Count.Should().Be(1); - observer.Received.Single().Payload.CorrelationId.ToCorrelationId().Id.Should().Be(signedMessage.CorrelationId.ToCorrelationId().Id); + observer.Received.Single().CorrelationId.ToCorrelationId().Id.Should().Be(signedMessage.CorrelationId.ToCorrelationId().Id); } } - [Fact] + [Test] public void Observer_Exception_Should_Not_Stop_Correct_Messages_Reception() { var testingChannel = new EmbeddedChannel("testWithExceptions".ToChannelId(), true, _factory.InheritedHandlers.ToArray()); - var serverIdentifier = PeerIdHelper.GetPeerId("server"); + var serverIdentifier = MultiAddressHelper.GetAddress("server"); var peerSettings = serverIdentifier.ToSubstitutedPeerSettings(); - using (var badHandler = new FailingRequestObserver(Substitute.For(), peerSettings)) + using (var badHandler = new FailingRequestObserver(Substitute.For(), peerSettings, _peerClient)) { var messageStream = GetObservableServiceHandler().MessageStream; badHandler.StartObserving(messageStream); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerIdValidatorTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerIdValidatorTests.cs index 7cdb5df4f9..3e85c09ddd 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerIdValidatorTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerIdValidatorTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,103 +22,42 @@ #endregion using System; -using System.Linq; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.P2P; using Catalyst.Core.Modules.Cryptography.BulletProofs; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; using FluentAssertions; -using Google.Protobuf; -using Xunit; -using Xunit.Abstractions; +using MultiFormats; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P { - public sealed class PeerIdValidatorTests + public class PeerIdValidatorTests { - private readonly ITestOutputHelper _output; - private readonly IPeerIdValidator _peerIdValidator; - private readonly PeerId _validPeerId; + private IPeerIdValidator _peerIdValidator; + private MultiAddress _validPeerId; - public PeerIdValidatorTests(ITestOutputHelper output) + [SetUp] + public void Init() { - _output = output; - _validPeerId = PeerIdHelper.GetPeerId(); + _validPeerId = MultiAddressHelper.GetAddress(); _peerIdValidator = new PeerIdValidator(new FfiWrapper()); } - [Fact] + [Test] public void Can_Validate_PeerId_Format() { _peerIdValidator.ValidatePeerIdFormat(_validPeerId); - _output.WriteLine(string.Join(" ", _validPeerId.ToByteArray())); - var fieldsInBytes = new[] - { - _validPeerId.Ip.ToByteArray(), BitConverter.GetBytes(_validPeerId.Port), - _validPeerId.PublicKey.ToByteArray() - }; - _output.WriteLine(string.Join(" ", fieldsInBytes.SelectMany(b => b))); + TestContext.WriteLine(string.Join(" ", _validPeerId)); } - [Theory] - [InlineData(0)] - [InlineData(10)] - [InlineData(19)] - [InlineData(21)] - public void Can_Throw_Argument_Exception_On_Invalid_Public_Key(int pubKeySize) + [Test] + public void Can_Throw_Argument_Exception_On_No_Public_Key() { - var invalidPeer = new PeerId(_validPeerId) - { - PublicKey = new byte[pubKeySize].ToByteString() - }; + var invalidPeer = new MultiAddress($"/ip4/77.68.110.78/tcp/4001/"); new Action(() => _peerIdValidator.ValidatePeerIdFormat(invalidPeer)) - .Should().Throw().WithMessage("*PublicKey*"); - - invalidPeer.PublicKey = new byte[21].ToByteString(); - } - - private sealed class IpTestData : TheoryData - { - public IpTestData() - { - Add(new byte[0]); - Add(new byte[4]); - Add(new byte[15]); - Add(new byte[17]); - } - } - - [Theory] - [ClassData(typeof(IpTestData))] - - //Todo: discuss if this is relevant: why do we enforce a given size for IPs (or anything) if proto handles it - public void Can_Throw_Argument_Exception_On_Invalid_Ip(byte[] ipBytes) - { - var invalidPeer = new PeerId(_validPeerId) - { - Ip = ipBytes.ToByteString() - }; - - new Action(() => _peerIdValidator.ValidatePeerIdFormat(invalidPeer)) - .Should().Throw().WithMessage("*Ip*"); - } - - [Theory] - [InlineData(0)] - [InlineData(1)] - [InlineData(1024)] - public void Can_Throw_Argument_Exception_On_Wrong_Port(ushort port) - { - var invalidPeer = new PeerId(_validPeerId) - { - Port = port - }; - - new Action(() => _peerIdValidator.ValidatePeerIdFormat(invalidPeer)) - .Should().Throw().WithMessage("*Port*"); + .Should().Throw().WithMessage("MultiAddress has no PeerId*"); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerServiceTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerServiceTests.cs index e46fb05fe6..63cd429bfc 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerServiceTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerServiceTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,61 +26,63 @@ using System.Linq; using System.Net; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.P2P.Discovery; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.EventLoop; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.P2P; using Catalyst.Protocol.Wire; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.P2P; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P { - public sealed class PeerServiceTests : SelfAwareTestBase, IDisposable + public class PeerServiceTests : SelfAwareTestBase, IDisposable { - private readonly ICorrelationId _guid; - private readonly ILogger _logger; - private readonly PeerId _pid; - private readonly IUdpServerChannelFactory _udpServerServerChannelFactory; - private readonly IPeerDiscovery _peerDiscovery; - private readonly List _p2PMessageHandlers; - private readonly EmbeddedObservableChannel _serverChannel; - private readonly IPeerSettings _peerSettings; + private ICorrelationId _guid; + private ILogger _logger; + private MultiAddress _pid; + private IUdpServerChannelFactory _udpServerServerChannelFactory; + private IPeerDiscovery _peerDiscovery; + private List _p2PMessageHandlers; + private EmbeddedObservableChannel _serverChannel; + private IPeerSettings _peerSettings; private IPeerService _peerService; - public PeerServiceTests(ITestOutputHelper output) : base(output) + [SetUp] + public void Init() { - _pid = PeerIdHelper.GetPeerId("im_a_key"); + this.Setup(TestContext.CurrentContext); + + _pid = MultiAddressHelper.GetAddress("im_a_key"); _guid = CorrelationId.GenerateCorrelationId(); _logger = Substitute.For(); _serverChannel = new EmbeddedObservableChannel($"Server:{CurrentTestName}"); - _udpServerServerChannelFactory = Substitute.For(); + _udpServerServerChannelFactory = Substitute.For>(); _peerSettings = Substitute.For(); _peerSettings.BindAddress.Returns(IPAddress.Parse("127.0.0.1")); _peerSettings.Port.Returns(1234); - _udpServerServerChannelFactory.BuildChannelAsync(Arg.Any(), _peerSettings.BindAddress, _peerSettings.Port) + _udpServerServerChannelFactory.BuildChannelAsync(Arg.Any(), _peerSettings.Address) .Returns(_serverChannel); _peerDiscovery = Substitute.For(); _p2PMessageHandlers = new List(); } - [Fact] + [Test] public async Task Can_receive_incoming_ping_responses() { var messageObserver = new TestMessageObserver(_logger); @@ -91,7 +93,7 @@ public async Task Can_receive_incoming_ping_responses() messageObserver.SubstituteObserver.Received().OnNext(Arg.Any()); } - [Fact] + [Test] public async Task Can_receive_PingRequest() { var pingRequestHandler = new TestMessageObserver(_logger); @@ -102,7 +104,7 @@ public async Task Can_receive_PingRequest() pingRequestHandler.SubstituteObserver.Received().OnNext(Arg.Any()); } - [Fact] + [Test] public async Task Can_receive_PeerNeighborsRequest() { var pingRequestHandler = new TestMessageObserver(_logger); @@ -113,11 +115,11 @@ public async Task Can_receive_PeerNeighborsRequest() pingRequestHandler.SubstituteObserver.Received().OnNext(Arg.Any()); } - [Fact] + [Test] public async Task Can_receive_PeerNeighborsResponse() { var pingRequestHandler = new TestMessageObserver(_logger); - var neighbourIds = "abc".Select(i => PeerIdHelper.GetPeerId(i.ToString())); + var neighbourIds = "abc".Select(i => MultiAddressHelper.GetAddress(i.ToString()).ToString()); var responseContent = new PeerNeighborsResponse(); responseContent.Peers.AddRange(neighbourIds); @@ -135,7 +137,7 @@ private async Task InitialisePeerServiceAndSendMessage(IP2PMessageObserver pingR { _p2PMessageHandlers.Add(pingRequestHandler); - _peerService = new PeerService(new UdpServerEventLoopGroupFactory(new EventLoopGroupFactoryConfiguration()), + _peerService = new DotnettyPeerService(new UdpServerEventLoopGroupFactory(new EventLoopGroupFactoryConfiguration()), _udpServerServerChannelFactory, _peerDiscovery, _p2PMessageHandlers, @@ -144,7 +146,7 @@ private async Task InitialisePeerServiceAndSendMessage(IP2PMessageObserver pingR Substitute.For()); await _peerService.StartAsync(); - _serverChannel.SimulateReceivingMessagesAsync(message); + _serverChannel.SimulateReceivingMessages(message); } private void Dispose(bool disposing) diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerTests.cs index a18e665438..f4a9d05c06 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/PeerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,17 +26,17 @@ using Catalyst.TestUtils; using FluentAssertions; using SharpRepository.InMemoryRepository; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P { public sealed class PeerTests { - [Fact] + [Test] public void EntityStoreAuditsCreateTime() { var repo = new InMemoryRepository(); - var peer = new Peer {PeerId = PeerIdHelper.GetPeerId("Test")}; + var peer = new Peer {Address = MultiAddressHelper.GetAddress("Test")}; repo.Add(peer); var retrievedPeer = repo.Get(peer.DocumentId); var now = DateTime.UtcNow.Date; @@ -48,11 +48,11 @@ public void EntityStoreAuditsCreateTime() retrievedPeer.Modified.Should().BeNull(); } - [Fact] + [Test] public void EntityStoreAuditsModifiedTime() { var repo = new InMemoryRepository(); - var peer = new Peer {PeerId = PeerIdHelper.GetPeerId("Test")}; + var peer = new Peer {Address = MultiAddressHelper.GetAddress("Test")}; repo.Add(peer); var retrievedPeer = repo.Get(peer.DocumentId); retrievedPeer.Touch(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/Protocols/PeerChallengeRequestTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/Protocols/PeerChallengeRequestTests.cs new file mode 100644 index 0000000000..caec3e947c --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/Protocols/PeerChallengeRequestTests.cs @@ -0,0 +1,98 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading.Tasks; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Core.Lib.P2P.Protocols; +using Catalyst.Core.Lib.Util; +using Catalyst.Protocol.Wire; +using Catalyst.TestUtils; +using FluentAssertions; +using NSubstitute; +using Serilog; +using NUnit.Framework; + + +namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.Protocols +{ + public class PeerChallengeRequestTests : SelfAwareTestBase + { + private IPeerChallengeRequest _peerChallengeRequest; + private IPeerSettings _testSettings; + private CancellationTokenProvider _cancellationProvider; + + [SetUp] + public void Init() + { + var subbedPeerClient = Substitute.For(); + _testSettings = PeerSettingsHelper.TestPeerSettings(); + _cancellationProvider = new CancellationTokenProvider(TimeSpan.FromSeconds(10)); + + _peerChallengeRequest = new PeerChallengeRequest( + Substitute.For(), + subbedPeerClient, + _testSettings, + 10 + ); + } + + [Test] + public async Task Can_Challenge_Peer() + { + var recipientAddress = MultiAddressHelper.GetAddress(); + await _peerChallengeRequest.ChallengePeerAsync(recipientAddress).ConfigureAwait(false); + await _peerChallengeRequest.PeerClient.ReceivedWithAnyArgs(1).SendMessageAsync(Arg.Is(x => x.Address == _testSettings.Address), Arg.Is(recipientAddress)); + } + + [Test] + public async Task Can_Receive_Query_Response_On_Observer() + { + var recipientAddress = MultiAddressHelper.GetAddress(); + var challengeResposne = new PeerChallengeResponse(recipientAddress); + + _peerChallengeRequest.ChallengeResponseMessageStreamer.OnNext(challengeResposne); + var response = await _peerChallengeRequest.ChallengePeerAsync(recipientAddress).ConfigureAwait(false); + response.Should().BeTrue(); + } + + [Test] + public async Task No_Response_Timeout_And_Returns_False() + { + var recipientAddress = MultiAddressHelper.GetAddress(); + _cancellationProvider.CancellationTokenSource.Cancel(); + var response = await _peerChallengeRequest.ChallengePeerAsync(recipientAddress).ConfigureAwait(false); + response.Should().BeFalse(); + } + + [Test] + public async Task Exception_During_Query_Returns_Null() + { + var recipientAddress = MultiAddressHelper.GetAddress(); + _cancellationProvider.Dispose(); //do summet nasty to force exception + var response = await _peerChallengeRequest.ChallengePeerAsync(recipientAddress).ConfigureAwait(false); + response.Should().BeFalse(); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/Protocols/PeerDeltaHistoryRequestTest.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/Protocols/PeerDeltaHistoryRequestTest.cs new file mode 100644 index 0000000000..f2d67b4907 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/Protocols/PeerDeltaHistoryRequestTest.cs @@ -0,0 +1,131 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.P2P.Protocols; +using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Hashing; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Wire; +using Catalyst.TestUtils; +using FluentAssertions; +using Google.Protobuf; +using MultiFormats.Registry; +using NSubstitute; +using Serilog; +using NUnit.Framework; + + +namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.Protocols +{ + public sealed class PeerDeltaHistoryRequestTest : SelfAwareTestBase + { + private IPeerDeltaHistoryRequest _peerDeltaHistoryRequest; + private IPeerSettings _testSettings; + private CancellationTokenProvider _cancellationProvider; + + [SetUp] + public void Init() + { + this.Setup(TestContext.CurrentContext); + + var subbedPeerClient = Substitute.For(); + _testSettings = PeerSettingsHelper.TestPeerSettings(); + _cancellationProvider = new CancellationTokenProvider(TimeSpan.FromSeconds(10)); + + _peerDeltaHistoryRequest = new PeerDeltaHistoryRequest( + Substitute.For(), + subbedPeerClient, + _testSettings, + _cancellationProvider + ); + } + + [Test] + public async Task Can_Query_Expected_Peer() + { + var recipientAddress = MultiAddressHelper.GetAddress(); + await _peerDeltaHistoryRequest.DeltaHistoryAsync(recipientAddress, 0, 10).ConfigureAwait(false); + await _peerDeltaHistoryRequest.PeerClient.ReceivedWithAnyArgs(1).SendMessageAsync(Arg.Is(x => x.Address == _testSettings.Address), Arg.Is(recipientAddress)); + } + + [Test] + public async Task Can_Receive_Query_Response_On_Observer() + { + var height = 10u; + var recipientPeerId = MultiAddressHelper.GetAddress(); + + var hp = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + var lastDeltaHash = hp.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32)); + + var collection = new List(); + + //// this matches the fake mock + for (uint x = 0; x < height; x++) + { + var delta = new Delta + { + PreviousDeltaDfsHash = lastDeltaHash.Digest.ToByteString() + }; + + var index = new DeltaIndex + { + Height = 10, + Cid = delta.ToByteString() + }; + + collection.Add(index); + lastDeltaHash = hp.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32)); + } + + var deltaHistoryResponse = new PeerDeltaHistoryResponse(recipientPeerId, collection); + + _peerDeltaHistoryRequest.DeltaHistoryResponseMessageStreamer.OnNext(deltaHistoryResponse); + var response = await _peerDeltaHistoryRequest.DeltaHistoryAsync(recipientPeerId, 0, height).ConfigureAwait(false); + response.DeltaCid.Count.Should().Be(10); + } + + [Test] + public async Task No_Response_Timeout_And_Returns_False() + { + var recipientPeerId = MultiAddressHelper.GetAddress(); + _cancellationProvider.CancellationTokenSource.Cancel(); + var response = await _peerDeltaHistoryRequest.DeltaHistoryAsync(recipientPeerId, 0, 10).ConfigureAwait(false); + response.Should().BeNull(); + } + + [Test] + public async Task Exception_During_Query_Returns_Null() + { + var recipientPeerId = MultiAddressHelper.GetAddress(); + _cancellationProvider.Dispose(); //do summet nasty to force exception + var response = await _peerDeltaHistoryRequest.DeltaHistoryAsync(recipientPeerId, 0, 10).ConfigureAwait(false); + response.Should().BeNull(); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/Protocols/PeerQueryTipTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/Protocols/PeerQueryTipTests.cs new file mode 100644 index 0000000000..ed95b879c5 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/Protocols/PeerQueryTipTests.cs @@ -0,0 +1,103 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading.Tasks; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Core.Lib.P2P.Protocols; +using Catalyst.Core.Lib.Util; +using Catalyst.Protocol.Wire; +using Catalyst.TestUtils; +using FluentAssertions; +using MultiFormats; +using NSubstitute; +using Serilog; +using NUnit.Framework; + +namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.Protocols +{ + public sealed class PeerQueryTipTests : SelfAwareTestBase + { + private IPeerQueryTipRequest _peerQueryTipRequest; + private IPeerSettings _testSettings; + private MultiAddress _recipientAddress; + private CancellationTokenProvider _cancellationProvider; + + [SetUp] + public void Init() + { + this.Setup(TestContext.CurrentContext); + + var subbedPeerClient = Substitute.For(); + _testSettings = PeerSettingsHelper.TestPeerSettings(); + _recipientAddress = _testSettings.Address; + + _cancellationProvider = new CancellationTokenProvider(TimeSpan.FromSeconds(10)); + + _peerQueryTipRequest = new PeerQueryTipRequestRequest( + Substitute.For(), + subbedPeerClient, + _testSettings, + _cancellationProvider + ); + } + + [Test] + public async Task Can_Query_Expected_Peer() + { + await _peerQueryTipRequest.QueryPeerTipAsync(_recipientAddress).ConfigureAwait(false); + await _peerQueryTipRequest.PeerClient.ReceivedWithAnyArgs(1).SendMessageAsync(Arg.Is(x => x.Address == _testSettings.Address), Arg.Is(_recipientAddress)); + } + + [Test] + public async Task Can_Receive_Query_Response_On_Observer() + { + var tipQueryResponse = new PeerQueryTipResponse(_recipientAddress, + MultiHash.ComputeHash(ByteUtil.GenerateRandomByteArray(32)) + ); + + _peerQueryTipRequest.QueryTipResponseMessageStreamer.OnNext(tipQueryResponse); + var response = await _peerQueryTipRequest.QueryPeerTipAsync(_recipientAddress).ConfigureAwait(false); + response.Should().BeTrue(); + } + + [Test] + public async Task No_Response_Timeout_And_Returns_False() + { + var recipientPeerId = MultiAddressHelper.GetAddress(); + _cancellationProvider.CancellationTokenSource.Cancel(); + var response = await _peerQueryTipRequest.QueryPeerTipAsync(recipientPeerId).ConfigureAwait(false); + response.Should().BeFalse(); + } + + [Test] + public async Task Exception_During_Query_Returns_Null() + { + var recipientPeerId = MultiAddressHelper.GetAddress(); + _cancellationProvider.Dispose(); //do summet nasty to force exception + var response = await _peerQueryTipRequest.QueryPeerTipAsync(recipientPeerId).ConfigureAwait(false); + response.Should().BeFalse(); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/ReputationSystem/ReputationManagerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/ReputationSystem/ReputationManagerTests.cs index 57f574161f..42e8c65b2b 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/ReputationSystem/ReputationManagerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/ReputationSystem/ReputationManagerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,155 +28,161 @@ using Catalyst.Abstractions.Config; using Catalyst.Abstractions.P2P.ReputationSystem; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.P2P.ReputationSystem; using Catalyst.TestUtils; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Core.Lib.Extensions; +using Nethermind.Core; namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.ReputationSystem { public sealed class ReputationManagerTests { - private readonly TestScheduler _testScheduler; - private readonly ILogger _subbedLogger; - private readonly IPeerRepository _subbedPeerRepository; + private TestScheduler _testScheduler; + private ILogger _subbedLogger; + private IPeerRepository _subbedPeerRepository; - public ReputationManagerTests() + [SetUp] + public void Init() { _testScheduler = new TestScheduler(); _subbedLogger = Substitute.For(); _subbedPeerRepository = Substitute.For(); } - [Fact] + [Test] public void Receiving_IPeerReputationChange_Can_Find_And_Update_Peer() { var peerReputationChange = Substitute.For(); - var pid = PeerIdHelper.GetPeerId("some_peer"); - peerReputationChange.PeerId.Returns(pid); + var pid = MultiAddressHelper.GetAddress("some_peer"); + peerReputationChange.Address.Returns(pid.GetKvmAddress()); peerReputationChange.ReputationEvent.Returns(Substitute.For()); peerReputationChange.ReputationEvent.Amount.Returns(100); - + var results = new List(); var subbedPeer = new Peer { - PeerId = pid + Address = pid, + KvmAddress = pid.GetKvmAddress() }; results.Add(subbedPeer); SetRepoReturnValue(results); - var reputationManager = new ReputationManager(_subbedPeerRepository, _subbedLogger); - + var reputationManager = new ReputationManager(_subbedPeerRepository, _subbedLogger, _testScheduler); + reputationManager.OnNext(peerReputationChange); _testScheduler.Start(); - _subbedPeerRepository.ReceivedWithAnyArgs(1).GetAll(); + _subbedPeerRepository.ReceivedWithAnyArgs(1).GetPeersByKvmAddress(Arg.Any
()); _subbedPeerRepository.ReceivedWithAnyArgs(1).Update(Arg.Is(subbedPeer)); } - - [Fact] + + [Test] public void Receiving_IPeerReputationChange_Can_Increase_Rep() { var peerReputationChange = Substitute.For(); - var pid = PeerIdHelper.GetPeerId("some_peer"); - peerReputationChange.PeerId.Returns(pid); + var pid = MultiAddressHelper.GetAddress("some_peer"); + peerReputationChange.Address.Returns(pid.GetKvmAddress()); peerReputationChange.ReputationEvent.Returns(Substitute.For()); peerReputationChange.ReputationEvent.Amount.Returns(100); - + var results = new List(); var subbedPeer = new Peer { - PeerId = pid, + Address = pid, + KvmAddress = pid.GetKvmAddress(), Reputation = 100 }; results.Add(subbedPeer); SetRepoReturnValue(results); - var reputationManager = new ReputationManager(_subbedPeerRepository, _subbedLogger); - + var reputationManager = new ReputationManager(_subbedPeerRepository, _subbedLogger, _testScheduler); + reputationManager.OnNext(peerReputationChange); _testScheduler.Start(); - _subbedPeerRepository.ReceivedWithAnyArgs(1).GetAll(); + _subbedPeerRepository.ReceivedWithAnyArgs(1).GetPeersByKvmAddress(Arg.Any
()); _subbedPeerRepository.ReceivedWithAnyArgs(1).Update(Arg.Is(r => r.Reputation == 200)); } - - [Fact] + + [Test] public void Receiving_IPeerReputationChange_Can_Decrease_Rep() { var peerReputationChange = Substitute.For(); - var pid = PeerIdHelper.GetPeerId("some_peer"); - peerReputationChange.PeerId.Returns(pid); + var pid = MultiAddressHelper.GetAddress("some_peer"); + peerReputationChange.Address.Returns(pid.GetKvmAddress()); peerReputationChange.ReputationEvent.Returns(Substitute.For()); peerReputationChange.ReputationEvent.Amount.Returns(-100); - + var results = new List(); var subbedPeer = new Peer { - PeerId = pid, + Address = pid, + KvmAddress = pid.GetKvmAddress(), Reputation = 100 }; results.Add(subbedPeer); SetRepoReturnValue(results); - var reputationManager = new ReputationManager(_subbedPeerRepository, _subbedLogger); - + var reputationManager = new ReputationManager(_subbedPeerRepository, _subbedLogger, _testScheduler); + reputationManager.OnNext(peerReputationChange); _testScheduler.Start(); - _subbedPeerRepository.ReceivedWithAnyArgs(1).GetAll(); + _subbedPeerRepository.ReceivedWithAnyArgs(1).GetPeersByKvmAddress(Arg.Any
()); _subbedPeerRepository.ReceivedWithAnyArgs(1).Update(Arg.Is(r => r.Reputation == 0)); } - [Fact] + [Test] public void Can_Merge_Streams_And_Read_Items_Pushed_On_Separate_Streams() { - var pid1 = PeerIdHelper.GetPeerId("peer1"); - var pid2 = PeerIdHelper.GetPeerId("peer2"); - + var pid1 = MultiAddressHelper.GetAddress("peer1"); + var pid2 = MultiAddressHelper.GetAddress("peer2"); + var results = new List(); - + var subbedPeer1 = new Peer { - PeerId = pid1, + Address = pid1, Reputation = 100 }; - + var peerReputationChangeEvent1 = Substitute.For(); - peerReputationChangeEvent1.PeerId.Returns(pid1); - + peerReputationChangeEvent1.Address.Returns(pid1.GetKvmAddress()); + var subbedPeer2 = new Peer { - PeerId = pid2, + Address = pid2, Reputation = 200 }; - + var peerReputationChangeEvent2 = Substitute.For(); - peerReputationChangeEvent2.PeerId.Returns(pid2); - + peerReputationChangeEvent2.Address.Returns(pid2.GetKvmAddress()); + results.Add(subbedPeer1); results.Add(subbedPeer2); - + SetRepoReturnValue(results); - var reputationManager = new ReputationManager(_subbedPeerRepository, _subbedLogger); - + var reputationManager = new ReputationManager(_subbedPeerRepository, _subbedLogger, _testScheduler); + var secondStreamSubject = new ReplaySubject(1, _testScheduler); var messageStream = secondStreamSubject.AsObservable(); messageStream.Subscribe(reputationChange => Substitute.For()); - + reputationManager.MergeReputationStream(messageStream); var streamObserver = Substitute.For>(); - + using (reputationManager.MergedEventStream.Subscribe(streamObserver.OnNext)) { reputationManager.ReputationEvent.OnNext(peerReputationChangeEvent1); @@ -184,14 +190,11 @@ public void Can_Merge_Streams_And_Read_Items_Pushed_On_Separate_Streams() _testScheduler.Start(); - streamObserver.Received(1).OnNext(Arg.Is(r => r.PeerId.Equals(pid1))); - streamObserver.Received(1).OnNext(Arg.Is(r => r.PeerId.Equals(pid2))); + streamObserver.Received(1).OnNext(Arg.Is(r => r.Address.Equals(pid1.GetKvmAddress()))); + streamObserver.Received(1).OnNext(Arg.Is(r => r.Address.Equals(pid2.GetKvmAddress()))); } } - - private void SetRepoReturnValue(IEnumerable list) - { - _subbedPeerRepository.GetAll().Returns(list); - } + + private void SetRepoReturnValue(IEnumerable list) { _subbedPeerRepository.GetPeersByKvmAddress(Arg.Any
()).Returns(list); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Registry/KeyRegistryTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Registry/KeyRegistryTests.cs index df69e43d18..32f089218c 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Registry/KeyRegistryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Registry/KeyRegistryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,28 +27,29 @@ using Catalyst.Core.Lib.Cryptography; using FluentAssertions; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Registry { public class KeyRegistryTests { - private readonly IPrivateKey _privateKey = Substitute.For(); - private readonly KeyRegistry _keyRegistry; + private IPrivateKey _privateKey = Substitute.For(); + private KeyRegistry _keyRegistry; - public KeyRegistryTests() + [SetUp] + public void Init() { _keyRegistry = new KeyRegistry(); } - [Fact] + [Test] public void Can_Add_Item_To_Registry() { _keyRegistry.AddItemToRegistry(KeyRegistryTypes.DefaultKey, _privateKey).Should().BeTrue(); _keyRegistry.RegistryContainsKey(KeyRegistryTypes.DefaultKey).Should().BeTrue(); } - [Fact] + [Test] public void Cant_Add_Items_To_Registry_Twice_With_Same_Key() { _keyRegistry.AddItemToRegistry(KeyRegistryTypes.DefaultKey, _privateKey).Should().BeTrue(); @@ -57,7 +58,7 @@ public void Cant_Add_Items_To_Registry_Twice_With_Same_Key() _keyRegistry.RegistryContainsKey(KeyRegistryTypes.DefaultKey).Should().BeTrue(); } - [Fact] + [Test] public void Can_Remove_Item_From_Registry() { _keyRegistry.AddItemToRegistry(KeyRegistryTypes.DefaultKey, _privateKey).Should().BeTrue(); @@ -66,20 +67,20 @@ public void Can_Remove_Item_From_Registry() _keyRegistry.RegistryContainsKey(KeyRegistryTypes.DefaultKey).Should().BeFalse(); } - [Fact] + [Test] public void Can_Retrieve_Item_From_Registry() { _keyRegistry.AddItemToRegistry(KeyRegistryTypes.DefaultKey, _privateKey).Should().BeTrue(); _keyRegistry.GetItemFromRegistry(KeyRegistryTypes.DefaultKey).Should().BeEquivalentTo(_privateKey); } - [Fact] + [Test] public void Retrieving_Item_Not_Contained_In_Registry_Returns_Null() { _keyRegistry.GetItemFromRegistry(KeyRegistryTypes.DefaultKey).Should().BeEquivalentTo((IPrivateKey) null); } - [Fact] + [Test] public void Cant_Add_Null_Item_To_Registry() { Action action = () => diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Registry/PasswordRegistryTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Registry/PasswordRegistryTests.cs index b4fde8d948..cd073473e6 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Registry/PasswordRegistryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Registry/PasswordRegistryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,69 +26,70 @@ using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Cryptography; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Registry { public sealed class PasswordRegistryTests : IDisposable { - private readonly SecureString _secureString = new SecureString(); - private readonly PasswordRegistry _passwordRegistry; + private SecureString _secureString = new SecureString(); + private PasswordRegistry _passwordRegistry; - public PasswordRegistryTests() + [SetUp] + public void Init() { _passwordRegistry = new PasswordRegistry(); } - [Fact] + [Test] public void Can_Add_Item_To_Registry() { - _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.IpfsPassword, _secureString).Should().BeTrue(); - _passwordRegistry.RegistryContainsKey(PasswordRegistryTypes.IpfsPassword).Should().BeTrue(); + _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.DefaultNodePassword, _secureString).Should().BeTrue(); + _passwordRegistry.RegistryContainsKey(PasswordRegistryTypes.DefaultNodePassword).Should().BeTrue(); } - [Fact] + [Test] public void Can_Add_Multiple_Items_To_Registry() { - _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.IpfsPassword, _secureString).Should().BeTrue(); + _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.DefaultNodePassword, _secureString).Should().BeTrue(); _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.CertificatePassword, _secureString).Should().BeTrue(); - _passwordRegistry.RegistryContainsKey(PasswordRegistryTypes.IpfsPassword).Should().BeTrue(); + _passwordRegistry.RegistryContainsKey(PasswordRegistryTypes.DefaultNodePassword).Should().BeTrue(); _passwordRegistry.RegistryContainsKey(PasswordRegistryTypes.CertificatePassword).Should().BeTrue(); } - [Fact] + [Test] public void Cant_Add_Items_To_Registry_Twice_With_Same_Key() { - _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.IpfsPassword, _secureString).Should().BeTrue(); - _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.IpfsPassword, _secureString).Should().BeFalse(); + _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.DefaultNodePassword, _secureString).Should().BeTrue(); + _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.DefaultNodePassword, _secureString).Should().BeFalse(); - _passwordRegistry.RegistryContainsKey(PasswordRegistryTypes.IpfsPassword).Should().BeTrue(); + _passwordRegistry.RegistryContainsKey(PasswordRegistryTypes.DefaultNodePassword).Should().BeTrue(); } - [Fact] + [Test] public void Can_Remove_Item_From_Registry() { - _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.IpfsPassword, _secureString).Should().BeTrue(); - _passwordRegistry.RemoveItemFromRegistry(PasswordRegistryTypes.IpfsPassword).Should().BeTrue(); + _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.DefaultNodePassword, _secureString).Should().BeTrue(); + _passwordRegistry.RemoveItemFromRegistry(PasswordRegistryTypes.DefaultNodePassword).Should().BeTrue(); - _passwordRegistry.RegistryContainsKey(PasswordRegistryTypes.IpfsPassword).Should().BeFalse(); + _passwordRegistry.RegistryContainsKey(PasswordRegistryTypes.DefaultNodePassword).Should().BeFalse(); } - [Fact] + [Test] public void Can_Retrieve_Item_From_Registry() { - _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.IpfsPassword, _secureString).Should().BeTrue(); - _passwordRegistry.GetItemFromRegistry(PasswordRegistryTypes.IpfsPassword).Should().BeEquivalentTo(_secureString); + _passwordRegistry.AddItemToRegistry(PasswordRegistryTypes.DefaultNodePassword, _secureString).Should().BeTrue(); + _passwordRegistry.GetItemFromRegistry(PasswordRegistryTypes.DefaultNodePassword).Should().BeEquivalentTo(_secureString); } - [Fact] + [Test] public void Retrieving_Item_Not_Contained_In_Registry_Returns_Null() { - _passwordRegistry.GetItemFromRegistry(PasswordRegistryTypes.IpfsPassword).Should().BeEquivalentTo((SecureString) null); + _passwordRegistry.GetItemFromRegistry(PasswordRegistryTypes.DefaultNodePassword).Should().BeEquivalentTo((SecureString) null); } - [Fact] + [Test] public void Cant_Add_Null_Item_To_Registry() { Action action = () => diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Repository/FileSystemAwareXmlRepositoryTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Repository/FileSystemAwareXmlRepositoryTests.cs index b7b0e1e78a..f3f02d000e 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Repository/FileSystemAwareXmlRepositoryTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Repository/FileSystemAwareXmlRepositoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,15 +23,15 @@ using System.IO; using Catalyst.Abstractions.FileSystem; -using Catalyst.Core.Lib.Repository; +using Catalyst.Core.Lib.Service; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Repository { public sealed class FileSystemAwareXmlRepositoryTests { - [Fact] + [Test] public void MyTestedMethod_Should_Be_Producing_This_Result_When_Some_Conditions_Are_Met() { var fileSystem = Substitute.For(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetPeerInfoRequestObserverTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetPeerInfoRequestObserverTests.cs deleted file mode 100644 index 668d25360f..0000000000 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetPeerInfoRequestObserverTests.cs +++ /dev/null @@ -1,194 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using System.Net; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Network; -using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; -using Catalyst.Core.Modules.Rpc.Server.IO.Observers; -using Catalyst.Protocol.Peer; -using Catalyst.Protocol.Wire; -using Catalyst.Protocol.Rpc.Node; -using Catalyst.TestUtils; -using DotNetty.Transport.Channels; -using FluentAssertions; -using Microsoft.Reactive.Testing; -using NSubstitute; -using Serilog; -using Xunit; - -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers -{ - /// - /// Tests the get peer info calls - /// - public sealed class GetPeerInfoRequestObserverTests - { - private readonly ILogger _logger; - private readonly IChannelHandlerContext _fakeContext; - private readonly IPeerRepository _peerRepository; - - public GetPeerInfoRequestObserverTests() - { - _logger = Substitute.For(); - _fakeContext = Substitute.For(); - - var fakeChannel = Substitute.For(); - _fakeContext.Channel.Returns(fakeChannel); - - var peers = GetPeerTestData(); - - _peerRepository = Substitute.For(); - _peerRepository.FindAll(Arg.Any>>()) - .Returns(ci => { return peers.Where(p => ((Expression>) ci[0]).Compile()(p)); }); - } - - public IEnumerable GetPeerTestData() - { - yield return new Peer - { - PeerId = - PeerIdHelper.GetPeerId("publickey-1", IPAddress.Parse("172.0.0.1"), 9090), - LastSeen = DateTime.UtcNow, Created = DateTime.UtcNow - }; - yield return new Peer - { - PeerId = - PeerIdHelper.GetPeerId("publickey-2", IPAddress.Parse("172.0.0.2"), 9090), - LastSeen = DateTime.UtcNow, Created = DateTime.UtcNow - }; - yield return new Peer - { - PeerId = - PeerIdHelper.GetPeerId("publickey-3", IPAddress.Parse("172.0.0.3"), 9090), - LastSeen = DateTime.UtcNow, Created = DateTime.UtcNow - }; - yield return new Peer - { - PeerId = - PeerIdHelper.GetPeerId("publickey-3", IPAddress.Parse("172.0.0.3"), 9090), - LastSeen = DateTime.UtcNow, Created = DateTime.UtcNow - }; - } - - /// - /// Tests the get peer info request and response via RPC. - /// Peer is expected to be found in this case - /// - /// Public key of the peer whose reputation is of interest - /// Ip address of the peer whose reputation is of interest - [Theory] - [InlineData("publickey-1", "172.0.0.1")] - [InlineData("publickey-2", "172.0.0.2")] - public void TestGetPeerInfoRequestSingularResponse(string publicKey, string ipAddress) - { - var peerId = PeerIdHelper.GetPeerId(publicKey, ipAddress, 12345); - var responseContent = GetPeerInfoTest(peerId); - responseContent.PeerInfo.Count().Should().Be(1); - - foreach (var peerInfo in responseContent.PeerInfo) - { - peerInfo.PeerId.Ip.ToByteArray().Should().BeEquivalentTo(peerId.Ip.ToByteArray()); - peerInfo.PeerId.PublicKey.ToByteArray().Should().BeEquivalentTo(peerId.PublicKey.ToByteArray()); - } - } - - /// - /// Tests the get peer info request and response via RPC. - /// Peer is expected to be found in this case - /// - /// Public key of the peer whose reputation is of interest - /// Ip address of the peer whose reputation is of interest - [Theory] - [InlineData("publickey-3", "172.0.0.3")] - public void TestGetPeerInfoRequestRepeatedResponse(string publicKey, string ipAddress) - { - var peerId = PeerIdHelper.GetPeerId(publicKey, ipAddress, 12345); - var responseContent = GetPeerInfoTest(peerId); - responseContent.PeerInfo.Count().Should().Be(2); - - foreach (var peerInfo in responseContent.PeerInfo) - { - peerInfo.PeerId.Ip.ToByteArray().Should().BeEquivalentTo(peerId.Ip.ToByteArray()); - peerInfo.PeerId.PublicKey.ToByteArray().Should().BeEquivalentTo(peerId.PublicKey.ToByteArray()); - } - } - - /// - /// Tests the get peer info request and response via RPC. - /// Peer is NOT expected to be found in this case, as they do not exist - /// - /// Public key of the peer whose reputation is of interest - /// Ip address of the peer whose reputation is of interest - [Theory] - [InlineData("this-pk-should-not-exist", "172.0.0.1")] - [InlineData("this-pk-should-not-exist", "172.0.0.3")] - [InlineData("publickey-1", "0.0.0.0")] - [InlineData("publickey-3", "0.0.0.0")] - public void TestGetPeerInfoRequestResponseForNonExistantPeers(string publicKey, string ipAddress) - { - var peerId = PeerIdHelper.GetPeerId(publicKey, ipAddress, 12345); - var responseContent = GetPeerInfoTest(peerId); - responseContent.PeerInfo.Count.Should().Be(0); - } - - /// - /// Tests the data/communication through protobuf - /// - /// - private GetPeerInfoResponse GetPeerInfoTest(PeerId peerId) - { - var testScheduler = new TestScheduler(); - - _fakeContext.Channel.RemoteAddress.Returns(EndpointBuilder.BuildNewEndPoint("192.0.0.1", 42042)); - - var senderPeerIdentifier = PeerIdHelper.GetPeerId("sender"); - var getPeerInfoRequest = new GetPeerInfoRequest {PublicKey = peerId.PublicKey, Ip = peerId.Ip}; - - var protocolMessage = - getPeerInfoRequest.ToProtocolMessage(senderPeerIdentifier); - - var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, protocolMessage); - - var peerSettings = senderPeerIdentifier.ToSubstitutedPeerSettings(); - var handler = new GetPeerInfoRequestObserver(peerSettings, _logger, _peerRepository); - - handler.StartObserving(messageStream); - - testScheduler.Start(); - - var receivedCalls = _fakeContext.Channel.ReceivedCalls().ToList(); - receivedCalls.Count.Should().Be(1); - - var sentResponseDto = (IMessageDto) receivedCalls[0].GetArguments().Single(); - - return sentResponseDto.Content.FromProtocolMessage(); - } - } -} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Shell/ConsoleUserOutputTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Shell/ConsoleUserOutputTests.cs index f02a69188d..1a244ddf56 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Shell/ConsoleUserOutputTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Shell/ConsoleUserOutputTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,13 +25,13 @@ using System.IO; using Catalyst.Core.Lib.Cli; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Shell { public sealed class ConsoleUserOutputTests { - [Fact] + [Test] public void WriteLine_Should_WriteLine_To_Console() { using (var sw = new StringWriter()) @@ -47,7 +47,7 @@ public void WriteLine_Should_WriteLine_To_Console() } } - [Fact] + [Test] public void Write_Should_Write_To_Console() { using (var sw = new StringWriter()) diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/ByteListComparerTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/ByteListComparerTests.cs index 92de460c4e..b9fd93f7a1 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/ByteListComparerTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/ByteListComparerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,13 +25,13 @@ using System.Text; using Catalyst.Core.Lib.Util; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Utils { public class ByteListComparerTests { - [Fact] + [Test] public void ByteListComparer_should_return_0_on_same_list() { var list1 = Encoding.UTF8.GetBytes("hello"); @@ -40,7 +40,7 @@ public void ByteListComparer_should_return_0_on_same_list() ByteUtil.ByteListMinSizeComparer.Default.Compare(list1, list2).Should().Be(0); } - [Fact] + [Test] public void ByteListComparer_should_return_0_on_same_beginning_of_list() { var list1 = Encoding.UTF8.GetBytes("hello"); @@ -49,7 +49,7 @@ public void ByteListComparer_should_return_0_on_same_beginning_of_list() ByteUtil.ByteListMinSizeComparer.Default.Compare(list1, list2).Should().Be(0); } - [Fact] + [Test] public void ByteListComparer_should_not_return_0_on_different_beginning_of_list() { var list1 = Encoding.UTF8.GetBytes("hello"); @@ -58,7 +58,7 @@ public void ByteListComparer_should_not_return_0_on_different_beginning_of_list( ByteUtil.ByteListMinSizeComparer.Default.Compare(list1, list2).Should().NotBe(0); } - [Fact] + [Test] public void ByteListComparer_should_return_one_on_different_on_higher_beginning_of_list() { var list1 = new byte[] {0, 1, 2, 3, 4, 5, 7, 8}; @@ -67,7 +67,7 @@ public void ByteListComparer_should_return_one_on_different_on_higher_beginning_ ByteUtil.ByteListMinSizeComparer.Default.Compare(list1, list2).Should().Be(1); } - [Fact] + [Test] public void ByteListComparer_should_return_minus_one_on_different_on_higher_beginning_of_list() { var list1 = new byte[] {0, 1, 2, 3, 4, 4, 7, 8}; @@ -76,7 +76,7 @@ public void ByteListComparer_should_return_minus_one_on_different_on_higher_begi ByteUtil.ByteListMinSizeComparer.Default.Compare(list1, list2).Should().Be(-1); } - [Fact] + [Test] public void ByteListComparer_should_return_one_if_only_second_arg_is_null() { var list1 = new byte[] { }; @@ -84,7 +84,7 @@ public void ByteListComparer_should_return_one_if_only_second_arg_is_null() ByteUtil.ByteListMinSizeComparer.Default.Compare(list1, null).Should().Be(1); } - [Fact] + [Test] public void ByteListComparer_should_return_minus_one_if_only_first_arg_is_null() { var list2 = new byte[] {3}; @@ -92,7 +92,7 @@ public void ByteListComparer_should_return_minus_one_if_only_first_arg_is_null() ByteUtil.ByteListMinSizeComparer.Default.Compare(null, list2).Should().Be(-1); } - [Fact] + [Test] public void ByteListComparer_should_return_0_when_both_args_are_null() { ByteUtil.ByteListMinSizeComparer.Default.Compare(null, null).Should().Be(0); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/ByteUtilTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/ByteUtilTests.cs index 0bb75bd3ff..eeed7a6c90 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/ByteUtilTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/ByteUtilTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,13 +25,13 @@ using System.Linq; using Catalyst.Core.Lib.Util; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Utils { public sealed class ByteUtilTests { - [Fact] + [Test] public void Merge_ByteArrays_To_Single_Byte_Collection_Should_Succeed() { var firstBytesArray = GeneratePopulatedBytesArray(50); @@ -46,7 +46,7 @@ public void Merge_ByteArrays_To_Single_Byte_Collection_Should_Succeed() byteResult.Length.Should().Be(100); } - [Fact] + [Test] public void Append_Byte_To_ByteArrays_Should_Succeed() { var firstBytesArray = GeneratePopulatedBytesArray(50); @@ -62,8 +62,8 @@ public void Append_Byte_To_ByteArrays_Should_Succeed() } [Theory] - [InlineData(78)] - [InlineData(10)] + [TestCase(78)] + [TestCase(10)] public void Initialise_Empty_ByteArray_Should_Succeed(int arraySize) { var byteResult = ByteUtil.InitialiseEmptyByteArray(arraySize); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/CancellationTokenProviderTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/CancellationTokenProviderTests.cs index 12b1a63f63..a49eccc6c6 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/CancellationTokenProviderTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/CancellationTokenProviderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,7 +24,7 @@ using System; using Catalyst.Core.Lib.Util; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Utils { @@ -34,10 +34,10 @@ public sealed class CancellationTokenProviderTests : IDisposable public CancellationTokenProviderTests() { - _cancellationTokenProvider = new CancellationTokenProvider(); + _cancellationTokenProvider = new CancellationTokenProvider(true); } - [Fact] + [Test] public void Has_Token_Cancelled_Should_Be_True_When_Cancelled() { _cancellationTokenProvider.HasTokenCancelled().Should().BeFalse(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/DateTimeUtilTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/DateTimeUtilTests.cs index 470a3804e1..5b1ed224e8 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/DateTimeUtilTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/DateTimeUtilTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,15 +22,15 @@ #endregion using System; -using Catalyst.Core.Lib.Util; +using Catalyst.Abstractions.Lib.Util; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Utils { public sealed class DateTimeUtilTests { - [Fact] + [Test] public void ExponentialTimeSpan_Should_Return_Exponential_TimeSpan() { var maxTimeSpan = TimeSpan.MaxValue; diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/DurationTest.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/DurationTest.cs new file mode 100644 index 0000000000..357b062f26 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/DurationTest.cs @@ -0,0 +1,115 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Core.Lib.Util; +using Catalyst.TestUtils; +using NUnit.Framework; + +namespace Catalyst.Core.Lib.Tests.UnitTests.Utils +{ + public class DurationTest + { + [Test] + public void Parsing_Examples() + { + Assert.AreEqual(TimeSpan.FromMilliseconds(300), Duration.Parse("300ms")); + Assert.AreEqual(TimeSpan.FromHours(-1.5), Duration.Parse("-1.5h")); + Assert.AreEqual(new TimeSpan(2, 45, 0), Duration.Parse("2h45m")); + Assert.AreEqual(new TimeSpan(0, 1, 0) + TimeSpan.FromSeconds(4.483878032), + Duration.Parse("1m4.483878032s")); + } + + [Test] + public void Parsing_Zero() + { + Assert.AreEqual(TimeSpan.Zero, Duration.Parse("0s")); + Assert.AreEqual(TimeSpan.Zero, Duration.Parse("0µs")); + Assert.AreEqual(TimeSpan.Zero, Duration.Parse("0ns")); + Assert.AreEqual(TimeSpan.Zero, Duration.Parse("n/a")); + Assert.AreEqual(TimeSpan.Zero, Duration.Parse("unknown")); + Assert.AreEqual(TimeSpan.Zero, Duration.Parse("")); + } + + [Test] + public void Parsing_Negative() { Assert.AreEqual(TimeSpan.FromHours(-2), Duration.Parse("-1.5h30m")); } + + [Test] + public void Parsing_Submilliseconds() + { + // Note: resolution of TimeSpan is 100ns, e.g. 1 tick. + Assert.AreEqual(TimeSpan.FromTicks(2), Duration.Parse("200ns")); + Assert.AreEqual(TimeSpan.FromTicks(2000), Duration.Parse("200us")); + Assert.AreEqual(TimeSpan.FromTicks(2000), Duration.Parse("200µs")); + } + + [Test] + public void Parsing_MissingNumber() + { + ExceptionAssert.Throws(() => + { + var _ = Duration.Parse("s"); + }); + } + + [Test] + public void Parsing_MissingUnit() + { + ExceptionAssert.Throws(() => + { + var _ = Duration.Parse("1"); + }, "Missing IPFS duration unit."); + } + + [Test] + public void Parsing_InvalidUnit() + { + ExceptionAssert.Throws(() => + { + var _ = Duration.Parse("1jiffy"); + }, "Unknown IPFS duration unit 'jiffy'."); + } + + [Test] + public void Stringify() + { + Assert.AreEqual("0s", Duration.Stringify(TimeSpan.Zero)); + Assert.AreEqual("n/a", Duration.Stringify(TimeSpan.Zero, "n/a")); + + Assert.AreEqual("2h", Duration.Stringify(new TimeSpan(2, 0, 0))); + Assert.AreEqual("3m", Duration.Stringify(new TimeSpan(0, 3, 0))); + Assert.AreEqual("4s", Duration.Stringify(new TimeSpan(0, 0, 4))); + Assert.AreEqual("5ms", Duration.Stringify(new TimeSpan(0, 0, 0, 0, 5))); + Assert.AreEqual("2h4s", Duration.Stringify(new TimeSpan(2, 0, 4))); + Assert.AreEqual("26h3m4s5ms", Duration.Stringify(new TimeSpan(1, 2, 3, 4, 5))); + + Assert.AreEqual("-48h", Duration.Stringify(TimeSpan.FromDays(-2))); + Assert.AreEqual("-2h", Duration.Stringify(TimeSpan.FromHours(-2))); + Assert.AreEqual("-1h30m", Duration.Stringify(TimeSpan.FromHours(-1.5))); + + Assert.AreEqual("200ns", Duration.Stringify(TimeSpan.FromTicks(2))); + Assert.AreEqual("200us", Duration.Stringify(TimeSpan.FromTicks(2000))); + Assert.AreEqual("200us300ns", Duration.Stringify(TimeSpan.FromTicks(2003))); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/EnumerableExtensionTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/EnumerableExtensionTests.cs index 7d3f35990b..5b8fcaff77 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/EnumerableExtensionTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/EnumerableExtensionTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,13 +26,13 @@ using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Utils { public static class EnumerableExtensionTests { - [Fact] + [Test] public static void RandomElementReturnsCorrectTypeOfString() { var randomList = new List(); @@ -42,7 +42,7 @@ public static void RandomElementReturnsCorrectTypeOfString() returnedElement.Should().BeOfType(); } - [Fact] + [Test] public static void RandomElementReturnsCorrectTypeOfBool() { var randomList = new List {false}; @@ -51,7 +51,7 @@ public static void RandomElementReturnsCorrectTypeOfBool() returnedElement.Should().BeFalse(); } - [Fact] + [Test] public static void GetARandomElement() { var randomList = new List(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/IpAddressConverterTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/IpAddressConverterTests.cs index cefa67f2b5..43fee94c2f 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/IpAddressConverterTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/IpAddressConverterTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,7 @@ using Catalyst.Core.Lib.Util; using FluentAssertions; using Newtonsoft.Json; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Utils { @@ -45,13 +45,13 @@ public IpAddressConverterTests() _ipAddressConverter = new IpAddressConverter(); } - [Fact] + [Test] public void Check_Convertibility_Must_Succeed() { _ipAddressConverter.CanConvert(IPAddress.Loopback.GetType()).Should().BeTrue(); } - [Fact] + [Test] public void Check_Convertibility_Should_Fail() { var ipAddressBytes = Encoding.ASCII.GetBytes("FalseData_Fake_Ip_Address_198.0.yplor"); @@ -59,7 +59,7 @@ public void Check_Convertibility_Should_Fail() _ipAddressConverter.CanConvert(ipAddressBytes.GetType()).Should().BeFalse(); } - [Fact] + [Test] public void Write_To_Json_Should_Succeed() { var ipTestObject = new JsonIpTester {Ip = IPAddress.Parse("127.0.0.1")}; @@ -67,7 +67,7 @@ public void Write_To_Json_Should_Succeed() serialized.Should().Contain("\"Ip\":\"127.0.0.1\""); } - [Fact] + [Test] public void Read_Json_Should_Succeed() { var testJson = "{ \"Ip\": \"127.0.0.1\" }"; @@ -76,7 +76,7 @@ public void Read_Json_Should_Succeed() .Ip.Should().Be(IPAddress.Parse("127.0.0.1")); } - [Fact] + [Test] public void Read_Json_Via_DeserializeObject_Should_Failed() { var testJson = "{ \"Ip\": \"127.0.0.1/\" }"; diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/IpEndPointConverterTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/IpEndPointConverterTests.cs new file mode 100644 index 0000000000..da27e00b36 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/IpEndPointConverterTests.cs @@ -0,0 +1,72 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Net; +using Catalyst.Core.Lib.Util; +using FluentAssertions; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Catalyst.Core.Lib.Tests.UnitTests.Utils +{ + public sealed class IpEndPointConverterTests + { + private readonly IpEndPointConverter _ipEndPointConverter; + private readonly IpAddressConverter _ipAddressConverter; + private readonly IPEndPoint _ipEndpoint; + private readonly string _ipEndPointJson; + + public IpEndPointConverterTests() + { + _ipEndPointConverter = new IpEndPointConverter(); + _ipAddressConverter = new IpAddressConverter(); + _ipEndpoint = new IPEndPoint(IPAddress.Loopback, 1000); + _ipEndPointJson = "{\"Address\":\"127.0.0.1\",\"Port\":1000}"; + } + + [Test] + public void Check_Convertibility_Must_Succeed() + { + _ipEndPointConverter.CanConvert(_ipEndpoint.GetType()).Should().BeTrue(); + } + + [Test] + public void Check_Convertibility_Should_Fail() + { + _ipEndPointConverter.CanConvert("bad type".GetType()).Should().BeFalse(); + } + + [Test] + public void Write_To_Json_Should_Succeed() + { + var serialized = JsonConvert.SerializeObject(_ipEndpoint, _ipEndPointConverter, _ipAddressConverter); + serialized.Should().Contain(_ipEndPointJson); + } + + [Test] + public void Read_Json_Should_Succeed() + { + JsonConvert.DeserializeObject(_ipEndPointJson, _ipEndPointConverter, _ipAddressConverter).Should().Be(_ipEndpoint); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/JsonProtoObjectConverterTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/JsonProtoObjectConverterTests.cs new file mode 100644 index 0000000000..fb2535956f --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/JsonProtoObjectConverterTests.cs @@ -0,0 +1,75 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Core.Lib.Util; +using Catalyst.Protocol.Wire; +using Catalyst.TestUtils; +using FluentAssertions; +using Google.Protobuf; +using Newtonsoft.Json; +using NUnit.Framework; + +namespace Catalyst.Core.Lib.Tests.UnitTests.Utils +{ + public sealed class JsonProtoObjectConverterTests + { + private readonly string _transactionJson; + private readonly TransactionBroadcast _transaction; + private readonly JsonProtoObjectConverter _jsonProtoObjectConverter; + + public JsonProtoObjectConverterTests() + { + _jsonProtoObjectConverter = new JsonProtoObjectConverter(); + _transaction = TransactionHelper.GetContractTransaction(ByteString.CopyFromUtf8("test"), 10, 10, 10); + _transactionJson = "{ \"publicEntry\": { \"receiverAddress\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAA=\", \"senderAddress\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAA=\", \"amount\": \"Cg==\", \"data\": \"dGVzdA==\", \"gasPrice\": \"Cg==\", \"gasLimit\": \"10\", \"signature\": { \"signingContext\": { \"networkType\": \"DEVNET\", \"signatureType\": \"TRANSACTION_PUBLIC\" }, \"rawBytes\": \"c2lnbmF0dXJl\" } } }"; + _transactionJson = "{ \"publicEntry\": { \"receiverAddress\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAA=\", \"senderAddress\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAA=\", \"amount\": \"Cg==\", \"data\": \"dGVzdA==\", \"gasPrice\": \"Cg==\", \"gasLimit\": \"10\", \"signature\": { \"signingContext\": { \"networkType\": \"DEVNET\", \"signatureType\": \"TRANSACTION_PUBLIC\" }, \"rawBytes\": \"c2lnbmF0dXJl\" } } }"; + _transactionJson = "{ \"publicEntry\": { \"receiverAddress\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAA=\", \"senderAddress\": \"AAAAAAAAAAAAAAAAAAAAAAAAAAA=\", \"amount\": \"Cg==\", \"data\": \"dGVzdA==\", \"gasPrice\": \"Cg==\", \"gasLimit\": \"10\", \"signature\": { \"signingContext\": { \"networkType\": \"DEVNET\", \"signatureType\": \"TRANSACTION_PUBLIC\" }, \"rawBytes\": \"c2lnbmF0dXJl\" } } }"; + } + + [Test] + public void Check_Convertibility_Must_Succeed() + { + _jsonProtoObjectConverter.CanConvert(_transaction.GetType()).Should().BeTrue(); + } + + [Test] + public void Check_Convertibility_Should_Fail() + { + _jsonProtoObjectConverter.CanConvert(_transaction.ToByteArray().GetType()).Should().BeFalse(); + } + + [Test] + public void Write_To_Json_Should_Succeed() + { + var serialized = JsonConvert.SerializeObject(_transaction, _jsonProtoObjectConverter); + serialized.Should().Contain(_transactionJson); + } + + [Test] + public void Read_Json_Should_Succeed() + { + JsonConvert.DeserializeObject(_transactionJson, _jsonProtoObjectConverter).Should() + .Be(_transaction); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/KeyUtilTest.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/KeyUtilTest.cs index 422d82629c..e0d0ca3cf2 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/KeyUtilTest.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/KeyUtilTest.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,13 +24,13 @@ using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Cryptography.BulletProofs; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Utils { public sealed class KeyUtilTest { - [Fact] + [Test] public void Can_Encode_And_Decode_Correctly() { var cryptoContext = new FfiWrapper(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/TtlChangeTokenProviderTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/TtlChangeTokenProviderTests.cs index fd3a81a185..829e73c6e7 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/TtlChangeTokenProviderTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Utils/TtlChangeTokenProviderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,7 +25,7 @@ using System.Threading; using Catalyst.Core.Lib.Util; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Utils { @@ -39,8 +39,8 @@ public TtlChangeTokenProviderTests() } [Theory] - [InlineData(500)] - [InlineData(1000)] + [TestCase(500)] + [TestCase(1000)] public void Can_Hit_Callback_When_Expired(int timeInMs) { var changeProvider = new TtlChangeTokenProvider(timeInMs); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Validators/ListValidatorReaderTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Validators/ListValidatorReaderTests.cs new file mode 100644 index 0000000000..bf3280a7d9 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Validators/ListValidatorReaderTests.cs @@ -0,0 +1,84 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Validators; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.Validators; +using FluentAssertions; +using Microsoft.Extensions.Configuration; +using NUnit.Framework; +using System.Collections.Generic; +using System.Linq; + +namespace Catalyst.Core.Lib.Tests.UnitTests.Validators +{ + [TestFixture] + public class ListValidatorReaderTests + { + [Test] + public void Can_Parse_Validator_List() + { + var json = @"{ + ""validators"": { + ""multi"": { + ""0"": { + ""list"": [ ""0x1a2149b4df5cbac970bc38fecc5237800c688c8b"" ] + }, + ""1"": { + ""list"": [ ""0x1a2149b4df5cbac970bc38fecc5237800c688c8c"" ] + }, + ""2"": { + ""contract"": ""0x79dd7e4c1b9adb07f71b54dba2d54db2fa549de3"" + } + } + } + }"; + + var validatorSets = new List(); + + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddJsonStream(json.ToMemoryStream()); + + var config = configurationBuilder.Build(); + + var multi = config.GetSection("validators:multi"); + var validatorSetAtStartBlocks = multi.GetChildren(); + + var validatorReader = new ListValidatorReader(); + + foreach (var validatorSetAtStartBlock in validatorSetAtStartBlocks) + { + if (validatorSetAtStartBlock.Key == null) + { + continue; + } + + var startBlock = long.Parse(validatorSetAtStartBlock.Key); + var property = validatorSetAtStartBlock.GetChildren().FirstOrDefault(); + validatorReader.AddValidatorSet(validatorSets, startBlock, property); + } + + validatorSets.Count.Should().Be(2); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Validators/TransactionValidatorTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Validators/TransactionValidatorTests.cs index e069ba4598..f4bafd8744 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Validators/TransactionValidatorTests.cs +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Validators/TransactionValidatorTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,144 +21,195 @@ #endregion +using System; +using System.Collections.Generic; using Catalyst.Abstractions.Cryptography; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Validators; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Network; using Catalyst.Protocol.Transaction; -using Catalyst.Protocol.Wire; using FluentAssertions; -using Google.Protobuf; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Core.Lib.Config; +using Catalyst.Abstractions.Config; namespace Catalyst.Core.Lib.Tests.UnitTests.Validators { + [TestFixture] public sealed class TransactionValidatorTests { - [Fact] - public void TransactionValidator_ValidateTransactionSignature_returns_false_when_signature_is_null() - { - var subbedLogger = Substitute.For(); - var subbedContext = Substitute.For(); + private ICryptoContext _cryptoContext; + private ITransactionConfig _transactionConfig; + private ILogger _logger; + private SigningContext _signingContext; - var transactionValidator = new TransactionValidator(subbedLogger, subbedContext); + [SetUp] + public void Init() + { + _logger = Substitute.For(); + _cryptoContext = Substitute.For(); + _transactionConfig = new TransactionConfig(); + _signingContext = new SigningContext + { + NetworkType = NetworkType.Devnet, + SignatureType = SignatureType.TransactionPublic + }; + } + [Test] + public void TransactionValidator_ValidateTransactionSignature_Returns_False_When_Signature_Is_Null() + { var invalidSignature = new Signature(); - var invalidTransactionBroadcast = new TransactionBroadcast + var invalidTransaction = new PublicEntry { Signature = invalidSignature, GasLimit = _transactionConfig.MinTransactionEntryGasLimit }; + var transactionValidator = new TransactionValidator(_cryptoContext, _transactionConfig, _logger); + + var result = transactionValidator.ValidateTransaction(invalidTransaction); + result.Should().BeFalse(); + } + + [Test] + public void TransactionValidator_ValidateTransactionGasLimit_Returns_False_When_Gas_Is_Under_Minimum() + { + var signatureResult = Substitute.For(); + var subbedContext = new FakeContext(signatureResult, true); + + signatureResult.SignatureBytes.Returns(new byte[64]); + + var transactionValidator = new TransactionValidator(subbedContext, _transactionConfig, _logger); + var privateKey = Substitute.For(); + + var invalidTransaction = new PublicEntry { - Signature = invalidSignature + SenderAddress = privateKey.GetPublicKey().Bytes.ToByteString(), + //Set gas limit to one less then minimum + GasLimit = _transactionConfig.MinTransactionEntryGasLimit-1 }; - var result = transactionValidator.ValidateTransaction(invalidTransactionBroadcast); + var signature = new Signature + { + //Sign an actual TransactionBroadcast object + RawBytes = subbedContext.Sign(privateKey, invalidTransaction, new SigningContext()) + .SignatureBytes.ToByteString(), + SigningContext = _signingContext + }; + + invalidTransaction.Signature = signature; + + var result = transactionValidator.ValidateTransaction(invalidTransaction); result.Should().BeFalse(); } - [Fact] + [Test] public void - TransactionValidator_ValidateTransactionSignature_returns_true_for_valid_transaction_signature_verification() + TransactionValidator_ValidateTransactionSignature_Returns_True_For_Valid_Transaction_Signature_Verification() { - var subbedLogger = Substitute.For(); - var subbedContext = Substitute.For(); - - subbedContext.GetSignatureFromBytes(Arg.Any(), Arg.Any()) - .ReturnsForAnyArgs(Substitute.For()); + var signatureResult = Substitute.For(); + var subbedContext = new FakeContext(signatureResult, true); - subbedContext.Verify(Arg.Any(), Arg.Any(), Arg.Any()) - .Returns(true); + signatureResult.SignatureBytes.Returns(new byte[64]); - subbedContext.Sign(Arg.Any(), Arg.Any(), Arg.Any()) - .SignatureBytes.Returns(new byte[64]); + var transactionValidator = new TransactionValidator(subbedContext, _transactionConfig, _logger); + var privateKey = Substitute.For(); - var transactionValidator = new TransactionValidator(subbedLogger, subbedContext); - var privateKey = subbedContext.GeneratePrivateKey(); - - var validTransactionBroadcast = new TransactionBroadcast + var validTransaction = new PublicEntry { - PublicEntries = - { - new PublicEntry - { - Base = new BaseEntry - { - SenderPublicKey = privateKey.GetPublicKey().Bytes.ToByteString() - } - } - } + SenderAddress = privateKey.GetPublicKey().Bytes.ToByteString(), + GasLimit = _transactionConfig.MinTransactionEntryGasLimit }; var signature = new Signature { - // sign an actual TransactionBroadcast object - RawBytes = subbedContext.Sign(privateKey, validTransactionBroadcast.ToByteArray(), Arg.Any()) + //Sign an actual TransactionBroadcast object + RawBytes = subbedContext.Sign(privateKey, validTransaction, new SigningContext()) .SignatureBytes.ToByteString(), - SigningContext = new SigningContext - { - NetworkType = NetworkType.Devnet, - SignatureType = SignatureType.TransactionPublic - } + SigningContext = _signingContext }; - validTransactionBroadcast.Signature = signature; + validTransaction.Signature = signature; - var result = transactionValidator.ValidateTransaction(validTransactionBroadcast); + var result = transactionValidator.ValidateTransaction(validTransaction); result.Should().BeTrue(); } - - [Fact] - public void TransactionValidator_ValidateTransactionSignature_returns_false_for_invalid_transaction_signature_verification() + + [Test] + public void + TransactionValidator_ValidateTransactionSignature_Returns_False_For_Invalid_Transaction_Signature_Verification() { - var subbedLogger = Substitute.For(); - var subbedContext = Substitute.For(); - - var privateKey = subbedContext.GeneratePrivateKey(); + var signatureResult = Substitute.For(); + var subbedContext = new FakeContext(signatureResult, false); + + signatureResult.SignatureBytes.Returns(new byte[64]); + + var privateKey = Substitute.For(); // raw un-signed tx message - var validTransactionBroadcast = new TransactionBroadcast + var validTransaction = new PublicEntry { - PublicEntries = - { - new PublicEntry - { - Base = new BaseEntry - { - SenderPublicKey = privateKey.GetPublicKey().Bytes.ToByteString() - } - } - } + SenderAddress = privateKey.GetPublicKey().Bytes.ToByteString(), + GasLimit = _transactionConfig.MinTransactionEntryGasLimit }; var txSig = new Signature { - RawBytes = new byte[64].ToByteString(), //random bytes that are not of a signed TransactionBroadcast Object - SigningContext = new SigningContext - { - NetworkType = NetworkType.Devnet, - SignatureType = SignatureType.TransactionPublic - } + RawBytes = new byte[64] + .ToByteString(), //random bytes that are not of a signed TransactionBroadcast Object + SigningContext = _signingContext }; - - subbedContext.GetSignatureFromBytes(Arg.Is(txSig.ToByteArray()), Arg.Is(privateKey.GetPublicKey().Bytes)) - .ReturnsForAnyArgs(Substitute.For()); - - subbedContext.Verify(Arg.Any(), // @TODO be more specific - Arg.Is(validTransactionBroadcast.ToByteArray()), - Arg.Is(txSig.SigningContext.ToByteArray()) - ) - .Returns(false); - - subbedContext.Sign(Arg.Is(privateKey), validTransactionBroadcast.ToByteArray(), txSig.SigningContext.ToByteArray()) - .SignatureBytes.Returns(new byte[64]); - - var transactionValidator = new TransactionValidator(subbedLogger, subbedContext); - - validTransactionBroadcast.Signature = txSig; - - var result = transactionValidator.ValidateTransaction(validTransactionBroadcast); + + var transactionValidator = new TransactionValidator(subbedContext, _transactionConfig, _logger); + + validTransaction.Signature = txSig; + + var result = transactionValidator.ValidateTransaction(validTransaction); result.Should().BeFalse(); } + + private sealed class FakeContext : ICryptoContext + { + private readonly ISignature _signature; + private readonly bool _verifyResult; + + public FakeContext(ISignature signature, bool verifyResult) + { + _signature = signature; + _verifyResult = verifyResult; + } + + public int PrivateKeyLength { get; } + public int PublicKeyLength { get; } + public int SignatureLength { get; } + public int SignatureContextMaxLength { get; } + public IPrivateKey GeneratePrivateKey() { throw new NotImplementedException(); } + + public IPublicKey GetPublicKeyFromPrivateKey(IPrivateKey privateKey) + { + throw new NotImplementedException(); + } + + public IPublicKey GetPublicKeyFromBytes(byte[] publicKeyBytes) { throw new NotImplementedException(); } + public IPrivateKey GetPrivateKeyFromBytes(byte[] privateKeyBytes) { throw new NotImplementedException(); } + public byte[] ExportPrivateKey(IPrivateKey privateKey) { throw new NotImplementedException(); } + public byte[] ExportPublicKey(IPublicKey publicKey) { throw new NotImplementedException(); } + + public ISignature Sign(IPrivateKey privateKey, ReadOnlySpan message, ReadOnlySpan context) + { + return _signature; + } + + public ISignature GetSignatureFromBytes(byte[] signatureBytes, byte[] publicKeyBytes) + { + return Substitute.For(); + } + + public bool Verify(ISignature signature, ReadOnlySpan message, ReadOnlySpan context) + { + return _verifyResult; + } + + public bool BatchVerify(IList signatures, IList messages, ReadOnlySpan context) { throw new NotImplementedException(); } + } } } - diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Validators/ValidatorSetStoreTests.cs b/src/Catalyst.Core.Lib.Tests/UnitTests/Validators/ValidatorSetStoreTests.cs new file mode 100644 index 0000000000..99eadd2841 --- /dev/null +++ b/src/Catalyst.Core.Lib.Tests/UnitTests/Validators/ValidatorSetStoreTests.cs @@ -0,0 +1,71 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Config; +using Catalyst.Abstractions.Validators; +using Catalyst.Core.Lib.Validators; +using FluentAssertions; +using NSubstitute; +using NUnit.Framework; +using System.Collections.Generic; + +namespace Catalyst.Core.Lib.Tests.UnitTests.Validators +{ + [TestFixture] + public class ValidatorSetStoreTests + { + [Test] + public void Can_Read_Validator_Set_From_Store() + { + var listValidatorSet = new ListValidatorSet(0, new[] { "0x1a2149b4df5cbac970bc38fecc5237800c688c8b" }); + var validatorSets = new List { listValidatorSet }; + + var validatorSetConfig = Substitute.For(); + validatorSetConfig.GetValidatorSetsAsync().Returns(validatorSets); + + var validatorSetStorer = new ValidatorSetStore(validatorSetConfig); + + validatorSetStorer.Get(0).GetValidators().Should().BeEquivalentTo(listValidatorSet.GetValidators()); + } + + [Test] + public void Can_Read_Validator_Set_From_Store_At_StartBlock() + { + var listValidatorSet1 = new ListValidatorSet(0, new[] { "0x1a2149b4df5cbac970bc38fecc5237800c688c8a" }); + var listValidatorSet2 = new ListValidatorSet(100, new[] { "0x1a2149b4df5cbac970bc38fecc5237800c688c8b", "0x1a2149b4df5cbac970bc38fecc5237800c688c8b" }); + + var validatorSets = new List { listValidatorSet1, listValidatorSet2 }; + + var validatorSetConfig = Substitute.For(); + validatorSetConfig.GetValidatorSetsAsync().Returns(validatorSets); + + var validatorSetStorer = new ValidatorSetStore(validatorSetConfig); + + validatorSetStorer.Get(0).GetValidators().Should().BeEquivalentTo(listValidatorSet1.GetValidators()); + validatorSetStorer.Get(50).GetValidators().Should().BeEquivalentTo(listValidatorSet1.GetValidators()); + + validatorSetStorer.Get(100).GetValidators().Should().BeEquivalentTo(listValidatorSet2.GetValidators()); + validatorSetStorer.Get(150).GetValidators().Should().BeEquivalentTo(listValidatorSet2.GetValidators()); + } + } +} diff --git a/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj b/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj index b14c0f5860..3027566057 100644 --- a/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj +++ b/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj @@ -1,12 +1,15 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Lib - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Lib.snk true + + 1701;1702;CS8002 + @@ -15,31 +18,34 @@ - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + - - - + + + + + + + diff --git a/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj.nuget.g.props b/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj.nuget.g.props deleted file mode 100644 index 4e7bae2e6b..0000000000 --- a/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj.nuget.g.props +++ /dev/null @@ -1,24 +0,0 @@ - - - - True - NuGet - /home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Lib/project.assets.json - /home/nsh/.nuget/packages/ - /home/nsh/.nuget/packages/ - PackageReference - 4.9.1 - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - SharpRepository.XmlRepository - 2.0.1-alpha3 - Content - False - repository.xml.json - - - \ No newline at end of file diff --git a/src/Catalyst.Core.Lib/Cli/ConsoleUserInput.cs b/src/Catalyst.Core.Lib/Cli/ConsoleUserInput.cs index 0f1b5a1624..bd6b89aa29 100644 --- a/src/Catalyst.Core.Lib/Cli/ConsoleUserInput.cs +++ b/src/Catalyst.Core.Lib/Cli/ConsoleUserInput.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Cli/ConsoleUserOutput.cs b/src/Catalyst.Core.Lib/Cli/ConsoleUserOutput.cs index 9fbdbc211d..304983aaee 100644 --- a/src/Catalyst.Core.Lib/Cli/ConsoleUserOutput.cs +++ b/src/Catalyst.Core.Lib/Cli/ConsoleUserOutput.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Config/ConfigApi.cs b/src/Catalyst.Core.Lib/Config/ConfigApi.cs new file mode 100644 index 0000000000..a6728fa4e0 --- /dev/null +++ b/src/Catalyst.Core.Lib/Config/ConfigApi.cs @@ -0,0 +1,138 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Options; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Catalyst.Core.Lib.Config +{ + public sealed class ConfigApi : IConfigApi + { + private static readonly JObject DefaultConfiguration = JObject.Parse(@"{ + ""Addresses"": { + ""API"": ""/ip4/127.0.0.1/tcp/5001"", + ""Gateway"": ""/ip4/127.0.0.1/tcp/8080"", + ""Swarm"": [ + ""/ip4/0.0.0.0/tcp/4001"", + ""/ip6/::/tcp/4001"" + ] + }, +}"); + + private JObject _configuration; + private readonly DfsOptions _dfsOptions; + + public ConfigApi(DfsOptions dfsOptions) { _dfsOptions = dfsOptions; } + + public async Task GetAsync(CancellationToken cancel = default) + { + // If first time, load the configuration into memory. + if (_configuration != null) + { + return _configuration; + } + + var path = Path.Combine(_dfsOptions.Repository.Folder, "config"); + if (File.Exists(path)) + { + using var reader = File.OpenText(path); + using JsonTextReader jtr = new(reader); + _configuration = await JObject.LoadAsync(jtr, cancel).ConfigureAwait(false); + } + else + { + await ReplaceAsync(DefaultConfiguration).ConfigureAwait(false); + } + + return _configuration; + } + + public async Task GetAsync(string key, CancellationToken cancel = default) + { + JToken config = await GetAsync(cancel).ConfigureAwait(false); + var keys = key.Split('.'); + foreach (var name in keys) + { + config = config[name]; + if (config == null) + { + throw new KeyNotFoundException($"Configuration setting '{key}' does not exist."); + } + } + + return config; + } + + public Task ReplaceAsync(JObject config) + { + _configuration = config ?? throw new ArgumentNullException(nameof(config)); + return SaveAsync(); + } + + public Task SetAsync(string key, string value, CancellationToken cancel = default) + { + return SetAsync(key, JToken.FromObject(value), cancel); + } + + public async Task SetAsync(string key, JToken value, CancellationToken cancel = default) + { + var config = await GetAsync(cancel).ConfigureAwait(false); + + // If needed, create the setting owner keys. + var keys = key.Split('.'); + foreach (var name in keys.Take(keys.Length - 1)) + { + if (!(config[name] is JObject token)) + { + token = new JObject(); + config[name] = token; + } + + config = token; + } + + config[keys.Last()] = value; + await SaveAsync().ConfigureAwait(false); + } + + private async Task SaveAsync() + { + var path = Path.Combine(_dfsOptions.Repository.Folder, "config"); + await using var fs = File.OpenWrite(path); + await using StreamWriter writer = new(fs); + using JsonTextWriter jtw = new(writer) + { + Formatting = Formatting.Indented + }; + await _configuration.WriteToAsync(jtw).ConfigureAwait(false); + } + } +} diff --git a/src/Catalyst.Core.Lib/Config/ConfigCopier.cs b/src/Catalyst.Core.Lib/Config/ConfigCopier.cs index 8e5c9fb08f..eec3ec953e 100644 --- a/src/Catalyst.Core.Lib/Config/ConfigCopier.cs +++ b/src/Catalyst.Core.Lib/Config/ConfigCopier.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -36,14 +36,14 @@ public abstract class ConfigCopier : IConfigCopier { /// public void RunConfigStartUp(string dataDir, - NetworkType networkType = NetworkType.Devnet, + NetworkType networkType, string sourceFolder = null, bool overwrite = false, string overrideNetworkFile = null) { Guard.Argument(dataDir, nameof(dataDir)).NotNull().NotEmpty().NotWhiteSpace(); - var dataDirInfo = new DirectoryInfo(dataDir); + DirectoryInfo dataDirInfo = new(dataDir); if (!dataDirInfo.Exists) { dataDirInfo.Create(); diff --git a/src/Catalyst.Core.Lib/Config/ConfigValueParser.cs b/src/Catalyst.Core.Lib/Config/ConfigValueParser.cs index 92c7dc426d..28cee79de1 100644 --- a/src/Catalyst.Core.Lib/Config/ConfigValueParser.cs +++ b/src/Catalyst.Core.Lib/Config/ConfigValueParser.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Config/Constants.cs b/src/Catalyst.Core.Lib/Config/Constants.cs index 6f482fcaed..1b77f4e6a7 100644 --- a/src/Catalyst.Core.Lib/Config/Constants.cs +++ b/src/Catalyst.Core.Lib/Config/Constants.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,6 +30,9 @@ public static class Constants // Folder with config files public static string ConfigSubFolder => "Config"; + // ValidatorSet configuration file + public static string ValidatorSetConfigFile => "validators.json"; + // Serilog configuration file public static string SerilogJsonConfigFile => "serilog.json"; diff --git a/src/Catalyst.Core.Lib/Config/DeltaConfig.cs b/src/Catalyst.Core.Lib/Config/DeltaConfig.cs new file mode 100644 index 0000000000..57aca22637 --- /dev/null +++ b/src/Catalyst.Core.Lib/Config/DeltaConfig.cs @@ -0,0 +1,32 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Config; + +namespace Catalyst.Core.Lib.Config +{ + public class DeltaConfig : IDeltaConfig + { + public ulong DeltaGasLimit => 8_000_000; + } +} diff --git a/src/Catalyst.Core.Lib/Config/NetworkTypeProvider.cs b/src/Catalyst.Core.Lib/Config/NetworkTypeProvider.cs new file mode 100644 index 0000000000..9bb5b7ff51 --- /dev/null +++ b/src/Catalyst.Core.Lib/Config/NetworkTypeProvider.cs @@ -0,0 +1,38 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Config; +using Catalyst.Protocol.Network; + +namespace Catalyst.Core.Lib.Config +{ + public class NetworkTypeProvider : INetworkTypeProvider + { + public NetworkType NetworkType { get; } + + public NetworkTypeProvider(NetworkType networkType) + { + NetworkType = networkType; + } + } +} diff --git a/src/Catalyst.Core.Lib/Config/TransactionConfig.cs b/src/Catalyst.Core.Lib/Config/TransactionConfig.cs new file mode 100644 index 0000000000..ba39294d28 --- /dev/null +++ b/src/Catalyst.Core.Lib/Config/TransactionConfig.cs @@ -0,0 +1,32 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Config; + +namespace Catalyst.Core.Lib.Config +{ + public class TransactionConfig : ITransactionConfig + { + public ulong MinTransactionEntryGasLimit => 21_000; + } +} diff --git a/src/Catalyst.Core.Lib/Config/ValidatorSetConfig.cs b/src/Catalyst.Core.Lib/Config/ValidatorSetConfig.cs new file mode 100644 index 0000000000..bc52ebcade --- /dev/null +++ b/src/Catalyst.Core.Lib/Config/ValidatorSetConfig.cs @@ -0,0 +1,68 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Config; +using Catalyst.Abstractions.Validators; +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; + +namespace Catalyst.Core.Lib.Config +{ + public class ValidatorSetConfig : IValidatorSetConfig + { + private readonly IConfigurationRoot _configuration; + private readonly IEnumerable _validatorReaders; + + public ValidatorSetConfig(IConfigurationRoot configuration, IEnumerable validatorReaders) + { + _configuration = configuration; + _validatorReaders = validatorReaders; + } + + public async Task> GetValidatorSetsAsync() + { + List validatorSets = new(); + var multi = _configuration.GetSection("validators:multi"); + var validatorSetAtStartBlocks = multi.GetChildren(); + + foreach (var validatorSetAtStartBlock in validatorSetAtStartBlocks) + { + foreach (var validatorReader in _validatorReaders) + { + if (validatorSetAtStartBlock.Key == null) + { + continue; + } + + var startBlock = long.Parse(validatorSetAtStartBlock.Key); + var property = validatorSetAtStartBlock.GetChildren().FirstOrDefault(); + validatorReader.AddValidatorSet(validatorSets, startBlock, property); + } + } + + return validatorSets; + } + } +} diff --git a/src/Catalyst.Core.Lib/CoreLibProvider.cs b/src/Catalyst.Core.Lib/CoreLibProvider.cs index ebdd8f7073..3644c503f1 100644 --- a/src/Catalyst.Core.Lib/CoreLibProvider.cs +++ b/src/Catalyst.Core.Lib/CoreLibProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,40 +22,39 @@ #endregion using Autofac; +using Catalyst.Abstractions.Config; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.FileSystem; -using Catalyst.Abstractions.FileTransfer; -using Catalyst.Abstractions.IO.EventLoop; using Catalyst.Abstractions.IO.Events; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.Keystore; using Catalyst.Abstractions.Network; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.P2P.Discovery; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; using Catalyst.Abstractions.P2P.Models; +using Catalyst.Abstractions.P2P.Protocols; using Catalyst.Abstractions.Rpc.IO.Messaging.Correlation; using Catalyst.Abstractions.Util; using Catalyst.Abstractions.Validators; +using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.Cryptography; -using Catalyst.Core.Lib.FileTransfer; -using Catalyst.Core.Lib.IO.EventLoop; using Catalyst.Core.Lib.IO.Events; using Catalyst.Core.Lib.P2P; using Catalyst.Core.Lib.P2P.Discovery; -using Catalyst.Core.Lib.P2P.IO.Messaging.Broadcast; using Catalyst.Core.Lib.P2P.IO.Messaging.Correlation; -using Catalyst.Core.Lib.P2P.IO.Transport.Channels; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Core.Lib.P2P.Protocols; using Catalyst.Core.Lib.P2P.ReputationSystem; using Catalyst.Core.Lib.Rpc.IO.Messaging.Correlation; using Catalyst.Core.Lib.Util; using Catalyst.Core.Lib.Validators; +using Catalyst.Protocol.Transaction; using DnsClient; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Options; +using LibP2P = Lib.P2P; + +// ReSharper disable WrongIndentSize namespace Catalyst.Core.Lib { @@ -67,58 +66,52 @@ public class CoreLibProvider : Module { protected override void Load(ContainerBuilder builder) { - // Register IO.EventLoop - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As(); - builder.RegisterType().As() - .WithProperty("TcpServerHandlerWorkerThreads", 4) - .WithProperty("TcpClientHandlerWorkerThreads", 4) - .WithProperty("UdpServerHandlerWorkerThreads", 8) - .WithProperty("UdpClientHandlerWorkerThreads", 2); - - // Register P2P - builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As(); + + + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As() - .WithParameter("peerChallengeWaitTimeSeconds", 5) + builder.RegisterType().As() + .WithParameter("ttl", 5) .SingleInstance(); + builder.RegisterType().As(); + + builder.RegisterType().As(); + builder.RegisterType().As(); + + builder.RegisterType().As(); + builder.RegisterType().As(); // Register P2P.Discovery builder.RegisterType().As(); - // Register P2P.IO.Transport.Channels - builder.RegisterType().As(); - builder.RegisterType().As(); // Register P2P.Messaging.Correlation builder.RegisterType().As().SingleInstance(); - // Register P2P.Messaging.Broadcast - builder.RegisterType().As().SingleInstance(); - - // Register P2P.Repository - builder.RegisterType().As().SingleInstance(); - // Register P2P.ReputationSystem builder.RegisterType().As().SingleInstance(); - + // Register Registry #inception builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); - + // Register Cryptography builder.RegisterType().As(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); // Register FileSystem builder.RegisterType().As().SingleInstance(); @@ -127,17 +120,14 @@ protected override void Load(ContainerBuilder builder) builder.RegisterType().As().SingleInstance(); // Register Utils - builder.RegisterType().As(); + builder.RegisterType().As() + .WithParameter("goodTillCancel", true); builder.RegisterType().As() .WithParameter("timeToLiveInMs", 8000); // Register Cache builder.RegisterType().As().SingleInstance(); builder.RegisterType().As>(); - - // Register file transfer - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); // Transaction validators builder.RegisterType().As().SingleInstance(); @@ -149,8 +139,14 @@ protected override void Load(ContainerBuilder builder) // Dns Client builder.RegisterType().As(); builder.RegisterType().As().UsingConstructor(); - + base.Load(builder); } + + // TODO: rethink validation of transaction signature + class AlwaysTrueTransactionValidator : ITransactionValidator + { + public bool ValidateTransaction(PublicEntry transaction) => true; + } } } diff --git a/src/Catalyst.Core.Lib/Cryptography/CertificateStore.cs b/src/Catalyst.Core.Lib/Cryptography/CertificateStore.cs index 1d126e48d9..5292fa97be 100644 --- a/src/Catalyst.Core.Lib/Cryptography/CertificateStore.cs +++ b/src/Catalyst.Core.Lib/Cryptography/CertificateStore.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -62,15 +62,6 @@ public X509Certificate2 ReadOrCreateCertificateFile(string pfxFilePath) return certificate; } - if (Environment.OSVersion.Platform == PlatformID.Unix) - { - throw new PlatformNotSupportedException( - "Catalyst network currently doesn't support on the fly creation of self signed certificate. " + - $"Please create a password protected certificate at {pfxFilePath}." + - Environment.NewLine + - "cf. `https://github.com/catalyst-network/Catalyst.Node/wiki/Creating-a-Self-Signed-Certificate` for instructions"); - } - certificate = CreateAndSaveSelfSignedCertificate(pfxFilePath); return certificate; @@ -110,7 +101,7 @@ private void Save(X509Certificate certificate, string fileName, SecureString pas private bool TryGet(string fileName, out X509Certificate2 certificate) { var fullPath = Path.Combine(_storageFolder.ToString(), fileName); - var fileInfo = new FileInfo(fullPath); + FileInfo fileInfo = new(fullPath); certificate = null; if (!fileInfo.Exists) @@ -162,16 +153,16 @@ private bool TryGet(string fileName, out X509Certificate2 certificate) public static X509Certificate2 BuildSelfSignedServerCertificate(SecureString password, string commonName = LocalHost) { - var sanBuilder = new SubjectAlternativeNameBuilder(); + SubjectAlternativeNameBuilder sanBuilder = new(); sanBuilder.AddIpAddress(IPAddress.Loopback); sanBuilder.AddIpAddress(IPAddress.IPv6Loopback); sanBuilder.AddDnsName(LocalHost); sanBuilder.AddDnsName(Environment.MachineName); - var distinguishedName = new X500DistinguishedName($"CN={commonName}"); + X500DistinguishedName distinguishedName = new($"CN={commonName}"); using (var rsa = RSA.Create(2048)) { - var request = new CertificateRequest(distinguishedName, rsa, HashAlgorithmName.SHA256, + CertificateRequest request = new(distinguishedName, rsa, HashAlgorithmName.SHA256, RSASignaturePadding.Pkcs1); request.CertificateExtensions.Add( diff --git a/src/Catalyst.Core.Lib/Cryptography/ConsolePasswordReader.cs b/src/Catalyst.Core.Lib/Cryptography/ConsolePasswordReader.cs index 483757fa48..72b357ca37 100644 --- a/src/Catalyst.Core.Lib/Cryptography/ConsolePasswordReader.cs +++ b/src/Catalyst.Core.Lib/Cryptography/ConsolePasswordReader.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -43,7 +43,7 @@ public ConsolePasswordReader(IUserOutput userOutput, IUserInput userInput) public SecureString ReadSecurePassword(string prompt) { - var pwd = new SecureString(); + SecureString pwd = new(); ReadCharsFromConsole(prompt, (c, i) => pwd.AppendChar(c), i => pwd.RemoveAt(i)); pwd.MakeReadOnly(); return pwd; diff --git a/src/Catalyst.Core.Lib/Cryptography/EncryptedKey.cs b/src/Catalyst.Core.Lib/Cryptography/EncryptedKey.cs new file mode 100644 index 0000000000..9cd2acee7a --- /dev/null +++ b/src/Catalyst.Core.Lib/Cryptography/EncryptedKey.cs @@ -0,0 +1,49 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Lib.Cryptography +{ + /// + /// A private key that is password protected. + /// + public class EncryptedKey + { + /// + /// The local name of the key. + /// + public string Name { get; set; } + + /// + /// The unique ID of the key. + /// + public string Id { get; set; } + + /// + /// PKCS #8 container. + /// + /// + /// Password protected PKCS #8 structure in the PEM formatw + /// + public string Pem { get; set; } + } +} diff --git a/src/Catalyst.Core.Lib/Cryptography/IsaacRandom.cs b/src/Catalyst.Core.Lib/Cryptography/IsaacRandom.cs index 0ee0b9ebae..3aeb77d5e1 100644 --- a/src/Catalyst.Core.Lib/Cryptography/IsaacRandom.cs +++ b/src/Catalyst.Core.Lib/Cryptography/IsaacRandom.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,7 @@ using System; using Catalyst.Abstractions.Cryptography; -using TheDotNetLeague.MultiFormats.MultiBase; +using MultiFormats; namespace Catalyst.Core.Lib.Cryptography { @@ -78,7 +78,7 @@ private void Isaac() } } - void Mix(ref uint a, ref uint b, ref uint c, ref uint d, ref uint e, ref uint f, ref uint g, ref uint h) + private void Mix(ref uint a, ref uint b, ref uint c, ref uint d, ref uint e, ref uint f, ref uint g, ref uint h) { a ^= b << 11; d += a; diff --git a/src/Catalyst.Core.Lib/Cryptography/KeyRegistry.cs b/src/Catalyst.Core.Lib/Cryptography/KeyRegistry.cs index 09c919fc6f..a0e3a81310 100644 --- a/src/Catalyst.Core.Lib/Cryptography/KeyRegistry.cs +++ b/src/Catalyst.Core.Lib/Cryptography/KeyRegistry.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Cryptography/NodePasswordRepeater.cs b/src/Catalyst.Core.Lib/Cryptography/NodePasswordRepeater.cs new file mode 100644 index 0000000000..eedf75b4ad --- /dev/null +++ b/src/Catalyst.Core.Lib/Cryptography/NodePasswordRepeater.cs @@ -0,0 +1,76 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Cli; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Types; +using System; +using System.Security; +using System.Threading.Tasks; + +namespace Catalyst.Core.Lib.Cryptography +{ + /// + /// Node password repeater. + /// This class will continuously prompt the user to input their password until the password is correct. + /// + public class NodePasswordRepeater : IPasswordRepeater + { + private readonly IKeyApi _keyApi; + private readonly IPasswordManager _passwordManager; + private readonly IUserOutput _userOutput; + + public NodePasswordRepeater(IKeyApi keyApi, IPasswordManager passwordManager, IUserOutput userOutput) + { + _keyApi = keyApi; + _passwordManager = passwordManager; + _userOutput = userOutput; + } + + /// + public async Task PromptAndReceiveAsync() + { + while (true) + { + try + { + var password = _passwordManager.PromptPassword(PasswordRegistryTypes.DefaultNodePassword, "Please provide your node password"); + await _keyApi.SetPassphraseAsync(password).ConfigureAwait(false); + return password; + } + catch (UnauthorizedAccessException) + { + _userOutput.WriteLine($"Invalid node password, please try again."); + } + } + } + + /// + public async Task PromptAndAddPasswordToRegistryAsync() + { + var password = await PromptAndReceiveAsync().ConfigureAwait(false); + _passwordManager.AddPasswordToRegistry(PasswordRegistryTypes.DefaultNodePassword, password); + } + } +} diff --git a/src/Catalyst.Core.Lib/Cryptography/PasswordManager.cs b/src/Catalyst.Core.Lib/Cryptography/PasswordManager.cs index 4c41aa1da1..177174c858 100644 --- a/src/Catalyst.Core.Lib/Cryptography/PasswordManager.cs +++ b/src/Catalyst.Core.Lib/Cryptography/PasswordManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -39,6 +39,12 @@ public PasswordManager(IPasswordReader passwordReader, IPasswordRegistry passwor _passwordRegistry = passwordRegistry; } + /// + public SecureString PromptPassword(PasswordRegistryTypes passwordType, string promptMessage = null) + { + return _passwordReader.ReadSecurePassword(promptMessage ?? $"Please enter your {passwordType.Name}"); + } + /// public SecureString RetrieveOrPromptPassword(PasswordRegistryTypes passwordType, string promptMessage = null) { diff --git a/src/Catalyst.Core.Lib/Cryptography/PasswordRegistry.cs b/src/Catalyst.Core.Lib/Cryptography/PasswordRegistry.cs index 9c72cafd6b..7f00e56ee7 100644 --- a/src/Catalyst.Core.Lib/Cryptography/PasswordRegistry.cs +++ b/src/Catalyst.Core.Lib/Cryptography/PasswordRegistry.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Cryptography/Proto/Key.cs b/src/Catalyst.Core.Lib/Cryptography/Proto/Key.cs new file mode 100644 index 0000000000..f5519821ce --- /dev/null +++ b/src/Catalyst.Core.Lib/Cryptography/Proto/Key.cs @@ -0,0 +1,58 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using ProtoBuf; + +namespace Catalyst.Core.Lib.Cryptography.Proto +{ + public enum KeyType + { + Rsa = 0, + Ed25519 = 1, + Secp256K1 = 2, + Ecdh = 4, + } + + [ProtoContract] + public sealed class PublicKey + { + [ProtoMember(1, IsRequired = true)] + public KeyType Type; + + [ProtoMember(2, IsRequired = true)] + public byte[] Data; + } + + // PrivateKey message is not currently used. Hopefully it never will be + // because it could introduce a huge security hole. +#if false + [ProtoContract] + class PrivateKey + { + [ProtoMember(1, IsRequired = true)] + public KeyType Type; + [ProtoMember(2, IsRequired = true)] + public byte[] Data; + } +#endif +} diff --git a/src/Catalyst.Core.Lib/Cryptography/SecureStringExtensions.cs b/src/Catalyst.Core.Lib/Cryptography/SecureStringExtensions.cs new file mode 100644 index 0000000000..97350d5509 --- /dev/null +++ b/src/Catalyst.Core.Lib/Cryptography/SecureStringExtensions.cs @@ -0,0 +1,59 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Runtime.InteropServices; +using System.Security; + +namespace Catalyst.Core.Lib.Cryptography +{ + /// + /// Extensions for a . + /// + public static class SecureStringExtensions + { + /// + /// Use the plain bytes of a . + /// + /// The secure string to access. + /// + /// A function to call with the plain bytes. + /// + public static void UseSecretBytes(this SecureString s, Action action) + { + var length = s.Length; + var p = SecureStringMarshal.SecureStringToGlobalAllocAnsi(s); + var plain = new byte[length]; + try + { + Marshal.Copy(p, plain, 0, length); + action(plain); + } + finally + { + Marshal.ZeroFreeGlobalAllocAnsi(p); + Array.Clear(plain, 0, length); + } + } + } +} diff --git a/src/Catalyst.Core.Lib/DAO/BaseEntryDao.cs b/src/Catalyst.Core.Lib/DAO/BaseEntryDao.cs deleted file mode 100644 index 2932b000b7..0000000000 --- a/src/Catalyst.Core.Lib/DAO/BaseEntryDao.cs +++ /dev/null @@ -1,69 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System.ComponentModel.DataAnnotations.Schema; -using AutoMapper; -using Catalyst.Abstractions.DAO; -using Catalyst.Core.Lib.DAO.Converters; -using Catalyst.Protocol.Transaction; - -// ReSharper disable UnusedAutoPropertyAccessor.Global -namespace Catalyst.Core.Lib.DAO -{ - public class BaseEntryDao : DaoBase - { - public ulong Nonce { get; set; } - public string ReceiverPublicKey { get; set; } - public string SenderPublicKey { get; set; } - public string TransactionFees { get; set; } - - [Column] - - // ReSharper disable once UnusedMember.Local - private TransactionBroadcastDao TransactionBroadcastDao { get; set; } - } - - public sealed class BaseEntryMapperInitialiser : IMapperInitializer - { - public void InitMappers(IMapperConfigurationExpression cfg) - { - cfg.CreateMap().ReverseMap(); - - cfg.CreateMap() - .ForMember(d => d.ReceiverPublicKey, - opt => opt.ConvertUsing(new ByteStringToStringPubKeyConverter(), s => s.ReceiverPublicKey)) - .ForMember(d => d.SenderPublicKey, - opt => opt.ConvertUsing(new ByteStringToStringPubKeyConverter(), s => s.SenderPublicKey)) - .ForMember(d => d.TransactionFees, - opt => opt.ConvertUsing(new ByteStringToUInt256StringConverter(), s => s.TransactionFees)); - - cfg.CreateMap() - .ForMember(d => d.ReceiverPublicKey, - opt => opt.ConvertUsing(new StringKeyUtilsToByteStringFormatter(), s => s.ReceiverPublicKey)) - .ForMember(d => d.SenderPublicKey, - opt => opt.ConvertUsing(new StringKeyUtilsToByteStringFormatter(), s => s.SenderPublicKey)) - .ForMember(d => d.TransactionFees, - opt => opt.ConvertUsing(new UInt256StringToByteStringConverter(), s => s.TransactionFees)); - } - } -} diff --git a/src/Catalyst.Core.Lib/DAO/ContractEntryDao.cs b/src/Catalyst.Core.Lib/DAO/ContractEntryDao.cs deleted file mode 100644 index 14eda7850a..0000000000 --- a/src/Catalyst.Core.Lib/DAO/ContractEntryDao.cs +++ /dev/null @@ -1,62 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System.ComponentModel.DataAnnotations.Schema; -using AutoMapper; -using Catalyst.Abstractions.DAO; -using Catalyst.Core.Lib.DAO.Converters; -using Catalyst.Protocol.Transaction; -using Google.Protobuf; - -namespace Catalyst.Core.Lib.DAO -{ - public class ContractEntryDao : DaoBase - { - public BaseEntryDao Base { get; set; } - public string Data { get; set; } - public string Amount { get; set; } - - [Column] - - // ReSharper disable once UnusedMember.Local - private TransactionBroadcastDao TransactionBroadcastDao { get; set; } - } - - public sealed class ContractEntryMapperInitialiser : IMapperInitializer - { - public void InitMappers(IMapperConfigurationExpression cfg) - { - { - cfg.CreateMap() - .ForMember(d => d.Amount, - opt => opt.ConvertUsing(new ByteStringToUInt256StringConverter(), s => s.Amount)) - .ForMember(e => e.Data, opt => opt.ConvertUsing()); - - cfg.CreateMap() - .ForMember(d => d.Amount, - opt => opt.ConvertUsing(new UInt256StringToByteStringConverter(), s => s.Amount)) - .ForMember(e => e.Data, opt => opt.ConvertUsing()); - } - } - } -} diff --git a/src/Catalyst.Core.Lib/DAO/Converters/Base64ByteStringsConverters.cs b/src/Catalyst.Core.Lib/DAO/Converters/Base64ByteStringsConverters.cs index 6e94f5b0b1..bf1b25a77b 100644 --- a/src/Catalyst.Core.Lib/DAO/Converters/Base64ByteStringsConverters.cs +++ b/src/Catalyst.Core.Lib/DAO/Converters/Base64ByteStringsConverters.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,6 +23,7 @@ using AutoMapper; using Google.Protobuf; +using MultiFormats; namespace Catalyst.Core.Lib.DAO.Converters { @@ -41,4 +42,20 @@ public ByteString Convert(string sourceMember, ResolutionContext context) return ByteString.FromBase64(sourceMember); } } + + public class ByteArrayToStringBase32Converter : IValueConverter + { + public string Convert(byte[] sourceMember, ResolutionContext context) + { + return sourceMember.ToBase32(); + } + } + + public class StringBase32ToByteArrayConverter : IValueConverter + { + public byte[] Convert(string sourceMember, ResolutionContext context) + { + return sourceMember.FromBase32(); + } + } } diff --git a/src/Catalyst.Core.Lib/DAO/Converters/ByteStringPubKeyConverters.cs b/src/Catalyst.Core.Lib/DAO/Converters/ByteStringBase32Converters.cs similarity index 85% rename from src/Catalyst.Core.Lib/DAO/Converters/ByteStringPubKeyConverters.cs rename to src/Catalyst.Core.Lib/DAO/Converters/ByteStringBase32Converters.cs index eb35da37e8..4ba5200f0f 100644 --- a/src/Catalyst.Core.Lib/DAO/Converters/ByteStringPubKeyConverters.cs +++ b/src/Catalyst.Core.Lib/DAO/Converters/ByteStringBase32Converters.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ namespace Catalyst.Core.Lib.DAO.Converters { - public class ByteStringToStringPubKeyConverter : IValueConverter + public class ByteStringToBase32Converter : IValueConverter { public string Convert(ByteString sourceMember, ResolutionContext context) { @@ -36,7 +36,7 @@ public string Convert(ByteString sourceMember, ResolutionContext context) } } - public class StringKeyUtilsToByteStringFormatter : IValueConverter + public class Base32ToByteStringFormatter : IValueConverter { public ByteString Convert(string sourceMember, ResolutionContext context) { diff --git a/src/Catalyst.Core.Lib/DAO/Converters/ByteStringBase58Converters.cs b/src/Catalyst.Core.Lib/DAO/Converters/ByteStringBase58Converters.cs new file mode 100644 index 0000000000..686f13ad83 --- /dev/null +++ b/src/Catalyst.Core.Lib/DAO/Converters/ByteStringBase58Converters.cs @@ -0,0 +1,46 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using AutoMapper; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.Util; +using Google.Protobuf; + +namespace Catalyst.Core.Lib.DAO.Converters +{ + public class ByteStringToBase58Converter : IValueConverter + { + public string Convert(ByteString sourceMember, ResolutionContext context) + { + return sourceMember.ToByteArray().KeyToString(); + } + } + + public class Base58ToByteStringFormatter : IValueConverter + { + public ByteString Convert(string sourceMember, ResolutionContext context) + { + return sourceMember.KeyToBytes().ToByteString(); + } + } +} diff --git a/src/Catalyst.Core.Lib/DAO/Converters/CorrelationIdByteStringConverters.cs b/src/Catalyst.Core.Lib/DAO/Converters/CorrelationIdByteStringConverters.cs index 8273ae815e..ab9388f6e0 100644 --- a/src/Catalyst.Core.Lib/DAO/Converters/CorrelationIdByteStringConverters.cs +++ b/src/Catalyst.Core.Lib/DAO/Converters/CorrelationIdByteStringConverters.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/DAO/Converters/DfsHashByteStringConverters.cs b/src/Catalyst.Core.Lib/DAO/Converters/DfsHashByteStringConverters.cs index 7f2ac60a11..b6c0431c6b 100644 --- a/src/Catalyst.Core.Lib/DAO/Converters/DfsHashByteStringConverters.cs +++ b/src/Catalyst.Core.Lib/DAO/Converters/DfsHashByteStringConverters.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,7 +24,7 @@ using AutoMapper; using Catalyst.Core.Lib.Extensions; using Google.Protobuf; -using TheDotNetLeague.MultiFormats.MultiBase; +using MultiFormats; namespace Catalyst.Core.Lib.DAO.Converters { diff --git a/src/Catalyst.Core.Lib/DAO/Converters/IpAddressByteStringConverters.cs b/src/Catalyst.Core.Lib/DAO/Converters/IpAddressByteStringConverters.cs index 71c74a7229..1aa7f627a7 100644 --- a/src/Catalyst.Core.Lib/DAO/Converters/IpAddressByteStringConverters.cs +++ b/src/Catalyst.Core.Lib/DAO/Converters/IpAddressByteStringConverters.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/DAO/Converters/TimestampToDateTimeConverter.cs b/src/Catalyst.Core.Lib/DAO/Converters/TimestampToDateTimeConverter.cs new file mode 100644 index 0000000000..9ddba279ee --- /dev/null +++ b/src/Catalyst.Core.Lib/DAO/Converters/TimestampToDateTimeConverter.cs @@ -0,0 +1,46 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using AutoMapper; +using Catalyst.Abstractions.DAO; +using Google.Protobuf.WellKnownTypes; + +namespace Catalyst.Core.Lib.DAO.Converters +{ + public class TimestampToDateTimeInitializer : IMapperInitializer + { + public void InitMappers(IMapperConfigurationExpression cfg) + { + cfg.CreateMap().ConvertUsing(); + cfg.CreateMap().ConvertUsing(); + } + + public class TimestampToDateTimeConverter : ITypeConverter, ITypeConverter + { + public DateTime Convert(Timestamp source, DateTime destination, ResolutionContext context) => source.ToDateTime(); + + public Timestamp Convert(DateTime source, Timestamp destination, ResolutionContext context) => Timestamp.FromDateTime(source); + } + } +} diff --git a/src/Catalyst.Core.Lib/DAO/Converters/UInt256ByteStringConverters.cs b/src/Catalyst.Core.Lib/DAO/Converters/UInt256ByteStringConverters.cs index d4820a8790..a0be331f84 100644 --- a/src/Catalyst.Core.Lib/DAO/Converters/UInt256ByteStringConverters.cs +++ b/src/Catalyst.Core.Lib/DAO/Converters/UInt256ByteStringConverters.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/DAO/SignatureDao.cs b/src/Catalyst.Core.Lib/DAO/Cryptography/SignatureDao.cs similarity index 83% rename from src/Catalyst.Core.Lib/DAO/SignatureDao.cs rename to src/Catalyst.Core.Lib/DAO/Cryptography/SignatureDao.cs index 2f51da339c..4aa339b4a6 100644 --- a/src/Catalyst.Core.Lib/DAO/SignatureDao.cs +++ b/src/Catalyst.Core.Lib/DAO/Cryptography/SignatureDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,7 +26,7 @@ using Catalyst.Core.Lib.DAO.Converters; using Catalyst.Protocol.Cryptography; -namespace Catalyst.Core.Lib.DAO +namespace Catalyst.Core.Lib.DAO.Cryptography { public class SignatureDao : DaoBase { @@ -40,11 +40,11 @@ public void InitMappers(IMapperConfigurationExpression cfg) { cfg.CreateMap() .ForMember(d => d.RawBytes, - opt => opt.ConvertUsing(new ByteStringToStringPubKeyConverter(), s => s.RawBytes)); + opt => opt.ConvertUsing(new ByteStringToBase32Converter(), s => s.RawBytes)); cfg.CreateMap() .ForMember(d => d.RawBytes, - opt => opt.ConvertUsing(new StringKeyUtilsToByteStringFormatter(), s => s.RawBytes)); + opt => opt.ConvertUsing(new Base32ToByteStringFormatter(), s => s.RawBytes)); } } } diff --git a/src/Catalyst.Core.Lib/DAO/SigningContextDao.cs b/src/Catalyst.Core.Lib/DAO/Cryptography/SigningContextDao.cs similarity index 94% rename from src/Catalyst.Core.Lib/DAO/SigningContextDao.cs rename to src/Catalyst.Core.Lib/DAO/Cryptography/SigningContextDao.cs index 07ee4c523b..7743ece6f9 100644 --- a/src/Catalyst.Core.Lib/DAO/SigningContextDao.cs +++ b/src/Catalyst.Core.Lib/DAO/Cryptography/SigningContextDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,7 @@ using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Network; -namespace Catalyst.Core.Lib.DAO +namespace Catalyst.Core.Lib.DAO.Cryptography { public sealed class SigningContextDao : DaoBase { diff --git a/src/Catalyst.Core.Lib/DAO/DaoBase.cs b/src/Catalyst.Core.Lib/DAO/DaoBase.cs index d80c03c8de..ce2b26c849 100644 --- a/src/Catalyst.Core.Lib/DAO/DaoBase.cs +++ b/src/Catalyst.Core.Lib/DAO/DaoBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,6 +22,7 @@ #endregion using System.ComponentModel.DataAnnotations; +using Newtonsoft.Json; using SharpRepository.Repository; namespace Catalyst.Core.Lib.DAO @@ -30,6 +31,7 @@ public abstract class DaoBase { [RepositoryPrimaryKey(Order = 1)] [Key] + [JsonProperty(PropertyName = "id")] public string Id { get; set; } } } diff --git a/src/Catalyst.Core.Lib/DAO/Deltas/CandidateDeltaBroadcastDao.cs b/src/Catalyst.Core.Lib/DAO/Deltas/CandidateDeltaBroadcastDao.cs index 9425c8d2d6..05c2a0eb3c 100644 --- a/src/Catalyst.Core.Lib/DAO/Deltas/CandidateDeltaBroadcastDao.cs +++ b/src/Catalyst.Core.Lib/DAO/Deltas/CandidateDeltaBroadcastDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,15 +24,17 @@ using AutoMapper; using Catalyst.Abstractions.DAO; using Catalyst.Core.Lib.DAO.Converters; +using Catalyst.Core.Lib.Extensions; using Catalyst.Protocol.Wire; using Google.Protobuf; +using Nethermind.Core; namespace Catalyst.Core.Lib.DAO.Deltas { public class CandidateDeltaBroadcastDao : DaoBase { public string Hash { get; set; } - public PeerIdDao ProducerId { get; set; } + public string Producer { get; set; } public string PreviousDeltaDfsHash { get; set; } } @@ -43,12 +45,16 @@ public void InitMappers(IMapperConfigurationExpression cfg) cfg.CreateMap() .ForMember(e => e.Hash, opt => opt.ConvertUsing()) + .ForMember(e => e.Producer, + opt => opt.MapFrom(x => new Address(x.Producer.ToByteArray()))) .ForMember(e => e.PreviousDeltaDfsHash, opt => opt.ConvertUsing()); cfg.CreateMap() .ForMember(e => e.Hash, opt => opt.ConvertUsing()) + .ForMember(e => e.Producer, + opt => opt.MapFrom(x => new Address(x.Producer).Bytes.ToByteString())) .ForMember(e => e.PreviousDeltaDfsHash, opt => opt.ConvertUsing()); } diff --git a/src/Catalyst.Core.Lib/DAO/Deltas/DeltaDao.cs b/src/Catalyst.Core.Lib/DAO/Deltas/DeltaDao.cs index 09f0239973..b1cecd67b3 100644 --- a/src/Catalyst.Core.Lib/DAO/Deltas/DeltaDao.cs +++ b/src/Catalyst.Core.Lib/DAO/Deltas/DeltaDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,6 +26,7 @@ using AutoMapper; using Catalyst.Abstractions.DAO; using Catalyst.Core.Lib.DAO.Converters; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Protocol.Deltas; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; diff --git a/src/Catalyst.Core.Lib/DAO/Deltas/DeltaDfsHashBroadcastDao.cs b/src/Catalyst.Core.Lib/DAO/Deltas/DeltaDfsHashBroadcastDao.cs index 917b6e3035..6e6c354bb4 100644 --- a/src/Catalyst.Core.Lib/DAO/Deltas/DeltaDfsHashBroadcastDao.cs +++ b/src/Catalyst.Core.Lib/DAO/Deltas/DeltaDfsHashBroadcastDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/DAO/Deltas/FavouriteDeltaBroadcastDao.cs b/src/Catalyst.Core.Lib/DAO/Deltas/FavouriteDeltaBroadcastDao.cs index 658c096f1f..5ef5a9c049 100644 --- a/src/Catalyst.Core.Lib/DAO/Deltas/FavouriteDeltaBroadcastDao.cs +++ b/src/Catalyst.Core.Lib/DAO/Deltas/FavouriteDeltaBroadcastDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,21 +23,28 @@ using AutoMapper; using Catalyst.Abstractions.DAO; +using Catalyst.Core.Lib.DAO.Peer; +using Catalyst.Core.Lib.Extensions; using Catalyst.Protocol.Wire; +using Nethermind.Core; namespace Catalyst.Core.Lib.DAO.Deltas { public class FavouriteDeltaBroadcastDao : DaoBase { public CandidateDeltaBroadcastDao Candidate { get; set; } - public PeerIdDao VoterId { get; set; } + public string Voter { get; set; } } public class FavouriteDeltaBroadcastMapperInitialiser : IMapperInitializer { public void InitMappers(IMapperConfigurationExpression cfg) { - cfg.CreateMap().ReverseMap(); + cfg.CreateMap() + .ForMember(e => e.Voter, opt => opt.MapFrom(x => new Address(x.Voter.ToByteArray()))); + + cfg.CreateMap() + .ForMember(e => e.Voter, opt => opt.MapFrom(x => new Address(x.Voter).Bytes.ToByteString())); } } } diff --git a/src/Catalyst.Core.Lib/DAO/IMapperProvider.cs b/src/Catalyst.Core.Lib/DAO/IMapperProvider.cs index badf3a5c3a..f7fa56b706 100644 --- a/src/Catalyst.Core.Lib/DAO/IMapperProvider.cs +++ b/src/Catalyst.Core.Lib/DAO/IMapperProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/DAO/Ledger/DeltaIndexDao.cs b/src/Catalyst.Core.Lib/DAO/Ledger/DeltaIndexDao.cs new file mode 100644 index 0000000000..89f9ecaccc --- /dev/null +++ b/src/Catalyst.Core.Lib/DAO/Ledger/DeltaIndexDao.cs @@ -0,0 +1,72 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using AutoMapper; +using Catalyst.Abstractions.DAO; +using Catalyst.Core.Lib.DAO.Converters; +using Catalyst.Protocol.Deltas; +using Google.Protobuf; +using Newtonsoft.Json; +using SharpRepository.Repository; + +namespace Catalyst.Core.Lib.DAO.Ledger +{ + public class DeltaIndexDao + { + [RepositoryPrimaryKey(Order = 1)] + [JsonProperty("id")] + public new string Id => BuildDocumentId(Height); + public ulong Height { set; get; } + public string Cid { set; get; } + + public static string BuildDocumentId(ulong number) => number.ToString("D"); + + public static TProto ToProtoBuff(DeltaIndexDao dao, IMapperProvider mapperProvider) + where TProto : IMessage + { + return mapperProvider.Mapper.Map(dao); + } + + public static DeltaIndexDao ToDao(DeltaIndex protoBuff, IMapperProvider mapperProvider) + where TProto : IMessage + { + return mapperProvider.Mapper.Map(protoBuff); + } + } + + public class DeltaIndexMapperInitialiser : IMapperInitializer + { + public void InitMappers(IMapperConfigurationExpression cfg) + { + cfg.CreateMap() + .ForMember(a => a.Height, opt => opt.UseDestinationValue()) + .ForMember(a => a.Cid, + opt => opt.ConvertUsing()); + + cfg.CreateMap() + .ForMember(a => a.Height, opt => opt.UseDestinationValue()) + .ForMember(a => a.Cid, + opt => opt.ConvertUsing()); + } + } +} diff --git a/src/Catalyst.Core.Lib/DAO/MapperProvider.cs b/src/Catalyst.Core.Lib/DAO/MapperProvider.cs index 2d5ca478c6..7a3fb47150 100644 --- a/src/Catalyst.Core.Lib/DAO/MapperProvider.cs +++ b/src/Catalyst.Core.Lib/DAO/MapperProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/DAO/PeerDao.cs b/src/Catalyst.Core.Lib/DAO/Peer/PeerDao.cs similarity index 83% rename from src/Catalyst.Core.Lib/DAO/PeerDao.cs rename to src/Catalyst.Core.Lib/DAO/Peer/PeerDao.cs index 7ef94e773a..3274bd7550 100644 --- a/src/Catalyst.Core.Lib/DAO/PeerDao.cs +++ b/src/Catalyst.Core.Lib/DAO/Peer/PeerDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,16 +24,15 @@ using System; using AutoMapper; using Catalyst.Abstractions.DAO; -using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.Repository.Attributes; -using Catalyst.Core.Lib.Util; +using Catalyst.Abstractions.Lib.Util; +using Catalyst.Abstractions.Service.Attributes; -namespace Catalyst.Core.Lib.DAO +namespace Catalyst.Core.Lib.DAO.Peer { [Audit] public sealed class PeerDao : DaoBase { - public PeerIdDao PeerIdentifier { get; set; } + public string Address { get; set; } public int Reputation { get; set; } @@ -59,16 +58,16 @@ public class PeerDaoMapperInitialiser : IMapperInitializer { public void InitMappers(IMapperConfigurationExpression cfg) { - cfg.CreateMap(); + cfg.CreateMap(); cfg.AllowNullDestinationValues = true; - cfg.CreateMap() + cfg.CreateMap() .ForMember(e => e.Reputation, opt => opt.UseDestinationValue()) .ForMember(e => e.BlackListed, opt => opt.UseDestinationValue()) .ForMember(e => e.Created, opt => opt.UseDestinationValue()) .ForMember(e => e.Modified, opt => opt.UseDestinationValue()) .ForMember(e => e.LastSeen, opt => opt.UseDestinationValue()) - .ForMember(e => e.PeerId, opt => opt.UseDestinationValue()); + .ForMember(e => e.Address, opt => opt.UseDestinationValue()); } } } diff --git a/src/Catalyst.Core.Lib/DAO/PeerIdDao.cs b/src/Catalyst.Core.Lib/DAO/Peer/PeerIdDao.cs similarity index 86% rename from src/Catalyst.Core.Lib/DAO/PeerIdDao.cs rename to src/Catalyst.Core.Lib/DAO/Peer/PeerIdDao.cs index db80c1a92e..565160a7e9 100644 --- a/src/Catalyst.Core.Lib/DAO/PeerIdDao.cs +++ b/src/Catalyst.Core.Lib/DAO/Peer/PeerIdDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,7 @@ using Catalyst.Protocol.Peer; using Google.Protobuf; -namespace Catalyst.Core.Lib.DAO +namespace Catalyst.Core.Lib.DAO.Peer { public class PeerIdDao : DaoBase { @@ -42,13 +42,13 @@ public void InitMappers(IMapperConfigurationExpression cfg) { cfg.CreateMap() .ForMember(e => e.PublicKey, - opt => opt.ConvertUsing()) + opt => opt.ConvertUsing()) .ForMember(e => e.Ip, opt => opt.ConvertUsing()); cfg.CreateMap() .ForMember(e => e.PublicKey, - opt => opt.ConvertUsing()) + opt => opt.ConvertUsing()) .ForMember(e => e.Ip, opt => opt.ConvertUsing()); } diff --git a/src/Catalyst.Core.Lib/DAO/ProtoDaoConversionExtensions.cs b/src/Catalyst.Core.Lib/DAO/ProtoDaoConversionExtensions.cs index d51429d4f8..853aeb172b 100644 --- a/src/Catalyst.Core.Lib/DAO/ProtoDaoConversionExtensions.cs +++ b/src/Catalyst.Core.Lib/DAO/ProtoDaoConversionExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ namespace Catalyst.Core.Lib.DAO public static class ProtoDaoConversionExtensions { public static TProto ToProtoBuff(this TDao dao, IMapperProvider mapperProvider) - where TDao : DaoBase + where TDao : DaoBase where TProto : IMessage { return mapperProvider.Mapper.Map(dao); diff --git a/src/Catalyst.Core.Lib/DAO/ProtocolErrorMessageDao.cs b/src/Catalyst.Core.Lib/DAO/ProtocolErrorMessageDao.cs index eff9fb951d..460f004fa5 100644 --- a/src/Catalyst.Core.Lib/DAO/ProtocolErrorMessageDao.cs +++ b/src/Catalyst.Core.Lib/DAO/ProtocolErrorMessageDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -33,7 +33,7 @@ namespace Catalyst.Core.Lib.DAO public class ProtocolErrorMessageDao : DaoBase { public Signature Signature { get; set; } - public PeerIdDao PeerId { get; set; } + public string Address { get; set; } public string CorrelationId { get; set; } public int Code { get; set; } } diff --git a/src/Catalyst.Core.Lib/DAO/ProtocolMessageDao.cs b/src/Catalyst.Core.Lib/DAO/ProtocolMessageDao.cs index ded41a97c5..60067fcb83 100644 --- a/src/Catalyst.Core.Lib/DAO/ProtocolMessageDao.cs +++ b/src/Catalyst.Core.Lib/DAO/ProtocolMessageDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,6 +24,7 @@ using AutoMapper; using Catalyst.Abstractions.DAO; using Catalyst.Core.Lib.DAO.Converters; +using Catalyst.Core.Lib.DAO.Cryptography; using Catalyst.Protocol.Wire; using Google.Protobuf; @@ -31,7 +32,7 @@ namespace Catalyst.Core.Lib.DAO { public class ProtocolMessageDao : DaoBase { - public PeerIdDao PeerId { get; set; } + public string Address { get; set; } public string CorrelationId { get; set; } public string TypeUrl { get; set; } public string Value { get; set; } diff --git a/src/Catalyst.Core.Lib/DAO/PublicEntryDao.cs b/src/Catalyst.Core.Lib/DAO/PublicEntryDao.cs deleted file mode 100644 index 76a80023d8..0000000000 --- a/src/Catalyst.Core.Lib/DAO/PublicEntryDao.cs +++ /dev/null @@ -1,56 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System.ComponentModel.DataAnnotations.Schema; -using AutoMapper; -using Catalyst.Abstractions.DAO; -using Catalyst.Core.Lib.DAO.Converters; -using Catalyst.Protocol.Transaction; - -namespace Catalyst.Core.Lib.DAO -{ - public class PublicEntryDao : DaoBase - { - public BaseEntryDao Base { get; set; } - public string Amount { get; set; } - - [Column] - - // ReSharper disable once UnusedMember.Local - private TransactionBroadcastDao TransactionBroadcastDao { get; set; } - } - - public sealed class PublicEntryMapperInitialiser : IMapperInitializer - { - public void InitMappers(IMapperConfigurationExpression cfg) - { - cfg.CreateMap() - .ForMember(d => d.Amount, - opt => opt.ConvertUsing(new ByteStringToUInt256StringConverter(), s => s.Amount)); - - cfg.CreateMap() - .ForMember(d => d.Amount, - opt => opt.ConvertUsing(new UInt256StringToByteStringConverter(), s => s.Amount)); - } - } -} diff --git a/src/Catalyst.Core.Lib/DAO/CoinbaseEntryDao.cs b/src/Catalyst.Core.Lib/DAO/Transaction/CoinbaseEntryDao.cs similarity index 77% rename from src/Catalyst.Core.Lib/DAO/CoinbaseEntryDao.cs rename to src/Catalyst.Core.Lib/DAO/Transaction/CoinbaseEntryDao.cs index 40a5bca24d..eb4b248b0f 100644 --- a/src/Catalyst.Core.Lib/DAO/CoinbaseEntryDao.cs +++ b/src/Catalyst.Core.Lib/DAO/Transaction/CoinbaseEntryDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,14 +24,16 @@ using AutoMapper; using Catalyst.Abstractions.DAO; using Catalyst.Core.Lib.DAO.Converters; +using Catalyst.Core.Lib.Extensions; using Catalyst.Protocol.Transaction; +using Nethermind.Core; -namespace Catalyst.Core.Lib.DAO +namespace Catalyst.Core.Lib.DAO.Transaction { public class CoinbaseEntryDao : DaoBase { public uint Version { get; set; } - public string ReceiverPublicKey { get; set; } + public string ReceiverKvmAddress { get; set; } public string Amount { get; set; } } @@ -42,14 +44,14 @@ public void InitMappers(IMapperConfigurationExpression cfg) cfg.CreateMap().ReverseMap(); cfg.CreateMap() - .ForMember(d => d.ReceiverPublicKey, - opt => opt.ConvertUsing(new ByteStringToStringPubKeyConverter(), s => s.ReceiverPublicKey)) + .ForMember(d => d.ReceiverKvmAddress, + opt => opt.MapFrom(s => new Address(s.ReceiverKvmAddress.ToByteArray()))) .ForMember(d => d.Amount, opt => opt.ConvertUsing(new ByteStringToUInt256StringConverter(), s => s.Amount)); cfg.CreateMap() - .ForMember(d => d.ReceiverPublicKey, - opt => opt.ConvertUsing(new StringKeyUtilsToByteStringFormatter(), s => s.ReceiverPublicKey)) + .ForMember(d => d.ReceiverKvmAddress, + opt => opt.MapFrom(s => new Address(s.ReceiverKvmAddress).Bytes.ToByteString())) .ForMember(d => d.Amount, opt => opt.ConvertUsing(new UInt256StringToByteStringConverter(), s => s.Amount)); } diff --git a/src/Catalyst.Core.Lib/DAO/ConfidentialEntryDao.cs b/src/Catalyst.Core.Lib/DAO/Transaction/ConfidentialEntryDao.cs similarity index 58% rename from src/Catalyst.Core.Lib/DAO/ConfidentialEntryDao.cs rename to src/Catalyst.Core.Lib/DAO/Transaction/ConfidentialEntryDao.cs index a752ee7881..42b3febecd 100644 --- a/src/Catalyst.Core.Lib/DAO/ConfidentialEntryDao.cs +++ b/src/Catalyst.Core.Lib/DAO/Transaction/ConfidentialEntryDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,17 +28,21 @@ using Catalyst.Protocol.Transaction; using Google.Protobuf; +// ReSharper disable WrongIndentSize // ReSharper disable UnusedAutoPropertyAccessor.Global -namespace Catalyst.Core.Lib.DAO +namespace Catalyst.Core.Lib.DAO.Transaction { public class ConfidentialEntryDao : DaoBase { - public BaseEntryDao Base { get; set; } + public ulong Nonce { get; set; } + public string ReceiverPublicKey { get; set; } + public string SenderPublicKey { get; set; } + public string TransactionFees { get; set; } public string PedersenCommitment { get; set; } public string RangeProof { get; set; } [Column] - + // ReSharper disable once UnusedMember.Local private TransactionBroadcastDao TransactionBroadcastDao { get; set; } } @@ -49,15 +53,27 @@ public void InitMappers(IMapperConfigurationExpression cfg) { cfg.CreateMap() .ForMember(e => e.RangeProof, - opt => opt.ConvertUsing()) + opt => opt.MapFrom(x => x.RangeProof.ToByteString().ToBase64())) .ForMember(e => e.PedersenCommitment, - opt => opt.ConvertUsing()); + opt => opt.ConvertUsing()) + .ForMember(d => d.ReceiverPublicKey, + opt => opt.ConvertUsing(new ByteStringToBase32Converter(), s => s.ReceiverPublicKey)) + .ForMember(d => d.SenderPublicKey, + opt => opt.ConvertUsing(new ByteStringToBase32Converter(), s => s.SenderPublicKey)) + .ForMember(d => d.TransactionFees, + opt => opt.ConvertUsing(new ByteStringToUInt256StringConverter(), s => s.TransactionFees)); cfg.CreateMap() .ForMember(e => e.RangeProof, - opt => opt.ConvertUsing()) + opt => opt.MapFrom(x => RangeProof.Parser.ParseFrom(ByteString.FromBase64(x.RangeProof)))) .ForMember(e => e.PedersenCommitment, - opt => opt.ConvertUsing()); + opt => opt.ConvertUsing()) + .ForMember(d => d.ReceiverPublicKey, + opt => opt.ConvertUsing(new Base32ToByteStringFormatter(), s => s.ReceiverPublicKey)) + .ForMember(d => d.SenderPublicKey, + opt => opt.ConvertUsing(new Base32ToByteStringFormatter(), s => s.SenderPublicKey)) + .ForMember(d => d.TransactionFees, + opt => opt.ConvertUsing(new UInt256StringToByteStringConverter(), s => s.TransactionFees)); } } } diff --git a/src/Catalyst.Core.Lib/DAO/Transaction/PublicEntryDao.cs b/src/Catalyst.Core.Lib/DAO/Transaction/PublicEntryDao.cs new file mode 100644 index 0000000000..b6ccc429d1 --- /dev/null +++ b/src/Catalyst.Core.Lib/DAO/Transaction/PublicEntryDao.cs @@ -0,0 +1,98 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.ComponentModel.DataAnnotations.Schema; +using AutoMapper; +using Catalyst.Abstractions.DAO; +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Repository; +using Catalyst.Core.Lib.DAO.Converters; +using Catalyst.Core.Lib.DAO.Cryptography; +using Catalyst.Protocol.Transaction; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; + +// ReSharper disable WrongIndentSize + +namespace Catalyst.Core.Lib.DAO.Transaction +{ + public class PublicEntryDao : DaoBase + { + public ulong Nonce { get; set; } + public string ReceiverAddress { get; set; } + public string SenderAddress { get; set; } + public string Data { get; set; } + public string Amount { get; set; } + public SignatureDao Signature { set; get; } + public string GasPrice { get; set; } + public ulong GasLimit { get; set; } + + [Column] + + // ReSharper disable once UnusedMember.Local + private TransactionBroadcastDao TransactionBroadcastDao { get; set; } + } + + public sealed class PublicEntryMapperInitialiser : IMapperInitializer + { + private readonly IHashProvider _hashProvider; + + public PublicEntryMapperInitialiser(IHashProvider hashProvider) { + _hashProvider = hashProvider; + } + + public void InitMappers(IMapperConfigurationExpression cfg) + { + cfg.AllowNullDestinationValues = true; + + cfg.CreateMap() + .ForMember(d => d.Id, opt => opt.MapFrom(src => src.GetDocumentId(_hashProvider))) + .ForMember(d => d.Amount, + opt => opt.ConvertUsing(new ByteStringToBase32Converter(), s => s.Amount)) + .ForMember(e => e.Data, opt => opt.ConvertUsing()) + .ForMember(d => d.ReceiverAddress, + opt => opt.ConvertUsing(new ByteStringToBase32Converter(), s => s.ReceiverAddress)) + .ForMember(d => d.SenderAddress, + opt => opt.ConvertUsing(new ByteStringToBase32Converter(), s => s.SenderAddress)) + .ForMember(d => d.Nonce, opt => opt.MapFrom(s => s.Nonce)) + .ForMember(d => d.GasPrice, opt => opt.ConvertUsing(new ByteStringToBase32Converter(), s => s.GasPrice)) + .ForMember(d => d.GasLimit, opt => opt.MapFrom(s => s.GasLimit)); + + cfg.CreateMap() + .ForMember(d => d.Amount, + opt => opt.ConvertUsing(new Base32ToByteStringFormatter(), s => s.Amount)) + .ForMember(e => e.Data, opt => opt.ConvertUsing()) + .ForMember(d => d.ReceiverAddress, + opt => opt.ConvertUsing(new Base32ToByteStringFormatter(), s => s.ReceiverAddress)) + .ForMember(d => d.SenderAddress, + opt => opt.ConvertUsing(new Base32ToByteStringFormatter(), s => s.SenderAddress)) + .ForMember(d => d.Nonce, opt => opt.MapFrom(s => s.Nonce)) + .ForMember(d => d.GasPrice, opt => opt.ConvertUsing(new Base32ToByteStringFormatter(), s => s.GasPrice)) + .ForMember(d => d.GasLimit, opt => opt.MapFrom(s => s.GasLimit)); + + cfg.CreateMap().ConvertUsing(s => s.ToTimestamp()); + cfg.CreateMap().ConvertUsing(s => s.ToDateTime()); + } + } +} diff --git a/src/Catalyst.Core.Lib/DAO/TransactionBroadcastDao.cs b/src/Catalyst.Core.Lib/DAO/TransactionBroadcastDao.cs index 053c4ea469..85351acd82 100644 --- a/src/Catalyst.Core.Lib/DAO/TransactionBroadcastDao.cs +++ b/src/Catalyst.Core.Lib/DAO/TransactionBroadcastDao.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,35 +21,22 @@ #endregion -using System; -using System.Collections.Generic; using AutoMapper; using Catalyst.Abstractions.DAO; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Protocol.Wire; -using Google.Protobuf.WellKnownTypes; using MongoDB.Bson.Serialization.Attributes; namespace Catalyst.Core.Lib.DAO { + /// + /// @TODO we shouldnt be saving TransactionBroadcast, this is a wire only object, + /// this should be mapped to a mempool object + /// [BsonIgnoreExtraElements] public class TransactionBroadcastDao : DaoBase { - private SignatureDao _signature; - - public SignatureDao Signature - { - get => _signature; - set - { - _signature = value; - Id = value.RawBytes; - } - } - - public DateTime TimeStamp { get; set; } - public IEnumerable PublicEntries { get; set; } - public IEnumerable ConfidentialEntries { get; set; } - public IEnumerable ContractEntries { get; set; } + public PublicEntryDao PublicEntry { get; set; } } public class TransactionBroadcastMapperInitialiser : IMapperInitializer @@ -60,12 +47,7 @@ public void InitMappers(IMapperConfigurationExpression cfg) cfg.AllowNullDestinationValues = true; cfg.CreateMap() - .ForMember(e => e.PublicEntries, opt => opt.UseDestinationValue()) - .ForMember(e => e.ContractEntries, opt => opt.UseDestinationValue()) - .ForMember(e => e.ConfidentialEntries, opt => opt.UseDestinationValue()); - - cfg.CreateMap().ConvertUsing(s => s.ToTimestamp()); - cfg.CreateMap().ConvertUsing(s => s.ToDateTime()); + .ForMember(e => e.PublicEntry, opt => opt.UseDestinationValue()); } } } diff --git a/src/Catalyst.Core.Lib/Dag/DagLink.cs b/src/Catalyst.Core.Lib/Dag/DagLink.cs new file mode 100644 index 0000000000..393b4098c9 --- /dev/null +++ b/src/Catalyst.Core.Lib/Dag/DagLink.cs @@ -0,0 +1,176 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using Catalyst.Abstractions.Dfs; +using Google.Protobuf; +using Lib.P2P; + +namespace Catalyst.Core.Lib.Dag +{ + /// + /// A link to another node in the IPFS Merkle DAG. + /// + public class DagLink : IMerkleLink + { + /// + /// Create a new instance of class. + /// + /// The name associated with the linked node. + /// The of the linked node. + /// The serialised size (in bytes) of the linked node. + public DagLink(string name, Cid id, long size) + { + Name = name; + Id = id; + Size = size; + } + + /// + /// Creates a new instance of the class from the + /// specified . + /// + /// + /// Some type of a Merkle link. + /// + public DagLink(IMerkleLink link) + { + Name = link.Name; + Id = link.Id; + Size = link.Size; + } + + /// + /// Creates a new instance of the class from the + /// specified . + /// + /// + /// A containing the binary representation of the + /// DagLink. + /// + public DagLink(Stream stream) { Read(stream); } + + /// + /// Creates a new instance of the class from the + /// specified . + /// + /// ( + /// A containing the binary representation of the + /// DagLink. + /// + public DagLink(CodedInputStream stream) { Read(stream); } + + /// + public string Name { get; private set; } + + /// + public Cid Id { get; private set; } + + /// + public long Size { get; private set; } + + /// + /// Writes the binary representation of the link to the specified . + /// + /// + /// The to write to. + /// + public void Write(Stream stream) + { + using (var cos = new CodedOutputStream(stream, true)) + { + Write(cos); + } + } + + /// + /// Writes the binary representation of the link to the specified . + /// + /// + /// The to write to. + /// + public void Write(CodedOutputStream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + stream.WriteTag(1, WireFormat.WireType.LengthDelimited); + Id.Write(stream); + + if (Name != null) + { + stream.WriteTag(2, WireFormat.WireType.LengthDelimited); + stream.WriteString(Name); + } + + stream.WriteTag(3, WireFormat.WireType.Varint); + stream.WriteInt64(Size); + } + + private void Read(Stream stream) + { + using (var cis = new CodedInputStream(stream, true)) + { + Read(cis); + } + } + + private void Read(CodedInputStream stream) + { + while (!stream.IsAtEnd) + { + var tag = stream.ReadTag(); + switch (WireFormat.GetTagFieldNumber(tag)) + { + case 1: + Id = Cid.Read(stream); + break; + case 2: + Name = stream.ReadString(); + break; + case 3: + Size = stream.ReadInt64(); + break; + default: + throw new InvalidDataException("Unknown field number"); + } + } + } + + /// + /// Returns the IPFS binary representation as a byte array. + /// + /// + /// A byte array. + /// + public byte[] ToArray() + { + using (var ms = new MemoryStream()) + { + Write(ms); + return ms.ToArray(); + } + } + } +} diff --git a/src/Catalyst.Core.Lib/Dag/DagNode.cs b/src/Catalyst.Core.Lib/Dag/DagNode.cs new file mode 100644 index 0000000000..aa5097ff86 --- /dev/null +++ b/src/Catalyst.Core.Lib/Dag/DagNode.cs @@ -0,0 +1,371 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Serialization; +using Catalyst.Abstractions.Dfs; +using Google.Protobuf; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Core.Lib.Dag +{ + /// + /// A node in the IPFS Merkle DAG. + /// + /// + /// A DagNode has opaque + /// and a set of navigable . + /// + [DataContract] + public sealed class DagNode : IDagNode, IEquatable + { + private Cid _id; + private string _hashAlgorithm = MultiHash.DefaultAlgorithmName; + private long? _size; + + /// + /// Create a new instance of a with the specified + /// and + /// + /// + /// The opaque data, can be null. + /// + /// + /// The links to other nodes. + /// + /// + /// The name of the hashing algorithm to use; defaults to + /// . + /// + public DagNode(byte[] data, + IEnumerable links = null, + string hashAlgorithm = MultiHash.DefaultAlgorithmName) + { + DataBytes = data ?? new byte[0]; + Links = (links ?? new DagLink[0]) + .OrderBy(link => link.Name ?? ""); + _hashAlgorithm = hashAlgorithm; + } + + /// + /// Creates a new instance of the class from the + /// specified . + /// + /// + /// A containing the binary representation of the + /// DagNode. + /// + public DagNode(Stream stream) { Read(stream); } + + /// + /// Creates a new instance of the class from the + /// specified . + /// + /// + /// ( + /// A containing the binary representation of the + /// DagNode. + /// + public DagNode(CodedInputStream stream) { Read(stream); } + + /// + [DataMember] + public IEnumerable Links { get; private set; } + + /// + [DataMember] + public byte[] DataBytes { get; private set; } + + /// + public Stream DataStream => new MemoryStream(DataBytes, false); + + /// + /// The serialised size in bytes of the node. + /// + [DataMember] + public long Size + { + get + { + if (!_size.HasValue) + { + ComputeSize(); + } + + return _size ?? throw new ArgumentNullException(); + } + } + + /// + [DataMember] + public Cid Id + { + get + { + if (_id == null) + { + ComputeHash(); + } + + return _id; + } + set + { + _id = value; + if (_id != null) + { + _hashAlgorithm = _id.Hash.Algorithm.Name; + } + } + } + + /// + public IMerkleLink ToLink(string name = "") { return new DagLink(name, Id, Size); } + + /// + /// Adds a link. + /// + /// + /// The link to add. + /// + /// + /// A new with the existing and new + /// links. + /// + /// + /// A DagNode is immutable. + /// + public IDagNode AddLink(IMerkleLink link) { return AddLinks(new[] {link}); } + + /// + /// Adds a sequence of links. + /// + /// + /// The sequence of links to add. + /// + /// + /// A new with the existing and new + /// links. + /// + /// + /// A DagNode is immutable. + /// + public IDagNode AddLinks(IEnumerable links) + { + var all = Links.Union(links); + return new DagNode(DataBytes, all, _hashAlgorithm); + } + + /// + /// Removes a link. + /// + /// + /// The to remove. + /// + /// + /// A new with the + /// removed. + /// + /// + /// A DagNode is immutable. + /// + /// No exception is raised if the does + /// not exist. + /// + /// + public DagNode RemoveLink(IMerkleLink link) { return RemoveLinks(new[] {link}); } + + /// + /// Remove a sequence of links. + /// + /// + /// The sequence of to remove. + /// + /// + /// A new with the + /// removed. + /// + /// + /// A DagNode is immutable. + /// + /// No exception is raised if any of the do + /// not exist. + /// + /// + public DagNode RemoveLinks(IEnumerable links) + { + var ignore = links.ToLookup(link => link.Id); + var some = Links.Where(link => !ignore.Contains(link.Id)); + return new DagNode(DataBytes, some, _hashAlgorithm); + } + + /// + /// Writes the binary representation of the node to the specified . + /// + /// + /// The to write to. + /// + public void Write(Stream stream) + { + using (var cos = new CodedOutputStream(stream, true)) + { + Write(cos); + } + } + + /// + /// Writes the binary representation of the node to the specified . + /// + /// + /// The to write to. + /// + public void Write(CodedOutputStream stream) + { + if (stream == null) + throw new ArgumentNullException(nameof(stream)); + + foreach (var link in Links.Select(l => new DagLink(l))) + { + using (var linkStream = new MemoryStream()) + { + link.Write(linkStream); + var msg = linkStream.ToArray(); + stream.WriteTag(2, WireFormat.WireType.LengthDelimited); + stream.WriteLength(msg.Length); + stream.WriteSomeBytes(msg); + } + } + + if (DataBytes.Length > 0) + { + stream.WriteTag(1, WireFormat.WireType.LengthDelimited); + stream.WriteLength(DataBytes.Length); + stream.WriteSomeBytes(DataBytes); + } + } + + private void Read(Stream stream) + { + using (var cis = new CodedInputStream(stream, true)) + { + Read(cis); + } + } + + private void Read(CodedInputStream stream) + { + var links = new List(); + var done = false; + + while (!stream.IsAtEnd && !done) + { + var tag = stream.ReadTag(); + switch (WireFormat.GetTagFieldNumber(tag)) + { + case 1: + DataBytes = stream.ReadSomeBytes(stream.ReadLength()); + done = true; + break; + case 2: + using (var ms = new MemoryStream(stream.ReadSomeBytes(stream.ReadLength()))) + { + links.Add(new DagLink(ms)); + } + + break; + default: + throw new InvalidDataException("Unknown field number"); + } + } + + if (DataBytes == null) + DataBytes = new byte[0]; + Links = links.ToArray(); + } + + /// + /// Returns the IPFS binary representation as a byte array. + /// + /// + /// A byte array. + /// + public byte[] ToArray() + { + using (var ms = new MemoryStream()) + { + Write(ms); + return ms.ToArray(); + } + } + + private void ComputeHash() + { + using (var ms = new MemoryStream()) + { + Write(ms); + _size = ms.Position; + ms.Position = 0; + _id = MultiHash.ComputeHash(ms, _hashAlgorithm); + } + } + + private void ComputeSize() + { + using (var ms = new MemoryStream()) + { + Write(ms); + _size = ms.Position; + } + } + + /// + public override bool Equals(object obj) + { + var that = obj as DagNode; + return that != null && Id == that.Id; + } + + /// + public bool Equals(DagNode that) { return that != null && Id == that.Id; } + + /// + /// TODO + /// + public static bool operator ==(DagNode a, DagNode b) + { + return ReferenceEquals(a, b) || !ReferenceEquals(a, null) && (!ReferenceEquals(b, null) && a.Equals(b)); + } + + /// + /// TODO + /// + public static bool operator !=(DagNode a, DagNode b) + { + return !ReferenceEquals(a, b) && (ReferenceEquals(a, null) || (ReferenceEquals(b, null) || !a.Equals(b))); + } + } +} diff --git a/src/Catalyst.Core.Lib/Extensions/ByteStringExtensions.cs b/src/Catalyst.Core.Lib/Extensions/ByteStringExtensions.cs index 0e642a3b9d..29bd19cd47 100644 --- a/src/Catalyst.Core.Lib/Extensions/ByteStringExtensions.cs +++ b/src/Catalyst.Core.Lib/Extensions/ByteStringExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Extensions/BytesExtensions.cs b/src/Catalyst.Core.Lib/Extensions/BytesExtensions.cs index 870a75b720..dfdd7df93f 100644 --- a/src/Catalyst.Core.Lib/Extensions/BytesExtensions.cs +++ b/src/Catalyst.Core.Lib/Extensions/BytesExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,15 +30,44 @@ using Catalyst.Protocol.Peer; using Dawn; using Google.Protobuf; +using Lib.P2P; +using MultiFormats; +using Nethermind.Core.Crypto; using Nethermind.Dirichlet.Numerics; namespace Catalyst.Core.Lib.Extensions { + public static class KeccakExtensions + { + public static ByteString ToByteString(this Keccak keccak) + { + return keccak == null ? ByteString.Empty : ByteString.CopyFrom(keccak.Bytes); + } + + public static Keccak ToKeccak(this ByteString byteString) + { + return (byteString == null || byteString.IsEmpty) ? null : new Keccak(byteString.ToByteArray()); + } + + public static Cid ToCid(this Keccak keccak) + { + Cid cid = new Cid + { + Version = 1, + Encoding = "base32", + ContentType = "dag-pb", + Hash = new MultiHash("keccak-256", keccak.Bytes) + }; + + return cid; + } + } + public static class BytesExtensions { public static MemoryStream ToMemoryStream(this byte[] content) { - var memoryStream = new MemoryStream(); + MemoryStream memoryStream = new(); memoryStream.Write(content, 0, content.Length); memoryStream.Seek(0, SeekOrigin.Begin); return memoryStream; diff --git a/src/Catalyst.Core.Lib/Extensions/DirectoryInfoExtensions.cs b/src/Catalyst.Core.Lib/Extensions/DirectoryInfoExtensions.cs index 20839ae34f..a9897803c2 100644 --- a/src/Catalyst.Core.Lib/Extensions/DirectoryInfoExtensions.cs +++ b/src/Catalyst.Core.Lib/Extensions/DirectoryInfoExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Extensions/EnumerableExtensions.cs b/src/Catalyst.Core.Lib/Extensions/EnumerableExtensions.cs index 67b7b85db0..359746845e 100644 --- a/src/Catalyst.Core.Lib/Extensions/EnumerableExtensions.cs +++ b/src/Catalyst.Core.Lib/Extensions/EnumerableExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -49,7 +49,7 @@ public static UInt256 Sum(this IEnumerable enumerable, Func se { Guard.Argument(enumerable, nameof(enumerable)).NotNull(); - var sum = new UInt256(); + UInt256 sum = new(); // ReSharper disable once LoopCanBeConvertedToQuery foreach (var item in enumerable) diff --git a/src/Catalyst.Core.Lib/Extensions/MultiAddressExtensions.cs b/src/Catalyst.Core.Lib/Extensions/MultiAddressExtensions.cs new file mode 100644 index 0000000000..d1e144eec8 --- /dev/null +++ b/src/Catalyst.Core.Lib/Extensions/MultiAddressExtensions.cs @@ -0,0 +1,71 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Core.Lib.Util; +using Google.Protobuf; +using MultiFormats; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Evm; +using System.Net; + +namespace Catalyst.Core.Lib.Extensions +{ + public static class MultiAddressExtensions + { + public static IPAddress GetIpAddress(this MultiAddress address) + { + return IPAddress.Parse(address.Protocols[0].Value); + } + + public static int GetPort(this MultiAddress address) + { + return int.Parse(address.Protocols[1].Value); + } + + public static string GetPublicKey(this MultiAddress address) + { + return address.GetPublicKeyBytes().KeyToString(); + } + + public static byte[] GetPublicKeyBytes(this MultiAddress address) + { + return address.PeerId.GetPublicKeyBytesFromPeerId(); + } + + public static Address GetKvmAddress(this MultiAddress address) + { + return new Address(ValueKeccak.Compute(GetPublicKeyBytes(address)).BytesAsSpan.SliceWithZeroPadding(0, 20).ToArray()); + } + + public static ByteString GetKvmAddressByteString(this MultiAddress address) + { + return GetKvmAddress(address).Bytes.ToByteString() ?? ByteString.Empty; + } + + public static IPEndPoint GetIPEndPoint(this MultiAddress address) + { + return new IPEndPoint(address.GetIpAddress(), address.GetPort()); + } + } +} diff --git a/src/Catalyst.Core.Lib/Extensions/NeighbourExtensions.cs b/src/Catalyst.Core.Lib/Extensions/NeighbourExtensions.cs index 305a32c5c9..6b61d2de9a 100644 --- a/src/Catalyst.Core.Lib/Extensions/NeighbourExtensions.cs +++ b/src/Catalyst.Core.Lib/Extensions/NeighbourExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,12 +26,13 @@ using Catalyst.Abstractions.P2P.Discovery; using Catalyst.Core.Lib.P2P.Discovery; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Core.Lib.Extensions { public static class NeighbourExtensions { - public static INeighbours ToNeighbours(this IEnumerable peerIdentifier) + public static INeighbours ToNeighbours(this IEnumerable peerIdentifier) { return new Neighbours(peerIdentifier.Select(p => new Neighbour(p) as INeighbour)); } diff --git a/src/Catalyst.Core.Lib/Extensions/PeerIdExtensions.cs b/src/Catalyst.Core.Lib/Extensions/PeerIdExtensions.cs new file mode 100644 index 0000000000..df24699370 --- /dev/null +++ b/src/Catalyst.Core.Lib/Extensions/PeerIdExtensions.cs @@ -0,0 +1,92 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Core.Lib.Cryptography.Proto; +using Catalyst.Core.Lib.Util; +using MultiFormats; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.X509; +using ProtoBuf; +using System; +using System.IO; + +namespace Catalyst.Core.Lib.Extensions +{ + public class PublicKeyExtractionException : Exception + { + public PublicKeyExtractionException(string message) : base(message) + { + + } + } + + public static class PeerIdExtensions + { + public static byte[] GetPublicKeyBytesFromPeerId(this byte[] peerIdBytes) + { + MultiHash peerId = new("id", peerIdBytes); + return GetPublicKeyBytesFromPeerId(peerId); + } + + public static byte[] GetPublicKeyBytesFromPeerId(this MultiHash peerId) + { + try + { + using MemoryStream ms = new(peerId.Digest); + var publicKey = Serializer.Deserialize(ms); + using Asn1InputStream aIn = new(publicKey.Data); + var info = SubjectPublicKeyInfo.GetInstance(aIn.ReadObject()); + return info.PublicKeyData.GetBytes(); + } + catch (Exception) + { + throw new PublicKeyExtractionException("Could not extract public key from peerId"); + } + } + + public static MultiHash ToPeerId(this byte[] publicKeyBytes) + { + Ed25519PublicKeyParameters publicKey = new(publicKeyBytes, 0); + var pksi = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(publicKey).GetDerEncoded(); + var pk = new PublicKey + { + Type = KeyType.Ed25519, + Data = pksi + }; + using MemoryStream ms = new(); + Serializer.Serialize(ms, pk); + MultiHash id = new("id", ms.ToArray()); + return id; + } + + public static MultiHash ToPeerId(this AsymmetricKeyParameter publicKeyParameter) + { + var ed25519PublicKeyParameter = (Ed25519PublicKeyParameters) publicKeyParameter; + return ToPeerId(ed25519PublicKeyParameter.GetEncoded()); + } + + } +} diff --git a/src/Catalyst.Core.Lib/Extensions/ProtobuffExtensions.cs b/src/Catalyst.Core.Lib/Extensions/ProtobuffExtensions.cs index 21646d6545..3bbb4b4c65 100644 --- a/src/Catalyst.Core.Lib/Extensions/ProtobuffExtensions.cs +++ b/src/Catalyst.Core.Lib/Extensions/ProtobuffExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -35,6 +35,7 @@ using Dawn; using Google.Protobuf; using Google.Protobuf.Reflection; +using MultiFormats; using Type = System.Type; namespace Catalyst.Core.Lib.Extensions @@ -47,16 +48,16 @@ public static class ProtobufExtensions public static readonly string RequestSuffix = "Request"; public static readonly string ResponseSuffix = "Response"; - private static readonly Dictionary ProtoToClrNameMapper = + private static readonly Dictionary ProtoToClrNameMapper = typeof(ProtocolMessage).Assembly.ExportedTypes .Where(t => typeof(IMessage).IsAssignableFrom(t)) .Select(t => ((IMessage) Activator.CreateInstance(t)).Descriptor) .ToDictionary(d => d.ShortenedFullName(), d => d.ClrType.FullName); - private static readonly List ProtoBroadcastAllowedMessages = + private static readonly List ProtoBroadcastAllowedMessages = ProtoToClrNameMapper.Keys.Where(t => t.EndsWith(BroadcastSuffix)).ToList(); - private static readonly List ProtoRequestAllowedMessages = + private static readonly List ProtoRequestAllowedMessages = ProtoToClrNameMapper.Keys.Where(t => t.EndsWith(RequestSuffix)).ToList(); private static readonly List ProtoResponseAllowedMessages = @@ -75,10 +76,10 @@ public static string ShortenedProtoFullName(this Type protoType) //get the static field Descriptor from T var descriptor = (MessageDescriptor) protoType .GetProperty("Descriptor", BindingFlags.Static | BindingFlags.Public) - .GetValue(null); + ?.GetValue(null); return ShortenedFullName(descriptor); } - + public static bool IsRequestType(this Type type) { var shortType = ShortenedProtoFullName(type); @@ -138,13 +139,13 @@ private static string SwapSuffixes(string requestTypeUrl, string originalSuffix, .Remove(requestTypeUrl.Length - originalSuffix.Length, originalSuffix.Length) + targetSuffix; } - + public static ProtocolMessage ToProtocolMessage(this IMessage protobufObject, - PeerId senderId, + MultiAddress sender, ICorrelationId correlationId = default) { var typeUrl = protobufObject.Descriptor.ShortenedFullName(); - Guard.Argument(senderId, nameof(senderId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); if (typeUrl.EndsWith(MessageTypes.Response.Name)) { @@ -153,7 +154,7 @@ public static ProtocolMessage ToProtocolMessage(this IMessage protobufObject, return new ProtocolMessage { - PeerId = senderId, + Address = sender.ToString(), CorrelationId = (correlationId?.Id ?? CorrelationId.GenerateCorrelationId().Id).ToByteString(), TypeUrl = typeUrl, Value = protobufObject.ToByteString() @@ -171,7 +172,7 @@ public static ICorrelationId ToCorrelationId(this ByteString guidBytes) return new CorrelationId(new Guid(validBytes)); } - + public static ByteString IpAddressToProtobuf(this IPAddress ipAddress) { return ByteString.CopyFrom(ipAddress.To16Bytes()); diff --git a/src/Catalyst.Core.Lib/Extensions/Protocol/Wire/ProtocolMessageExtensions.cs b/src/Catalyst.Core.Lib/Extensions/Protocol/Wire/ProtocolMessageExtensions.cs index a2fd189308..96ca5deb1c 100644 --- a/src/Catalyst.Core.Lib/Extensions/Protocol/Wire/ProtocolMessageExtensions.cs +++ b/src/Catalyst.Core.Lib/Extensions/Protocol/Wire/ProtocolMessageExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,7 +25,6 @@ using Catalyst.Abstractions.KeySigner; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Wire; -using Google.Protobuf; using Serilog; namespace Catalyst.Core.Lib.Extensions.Protocol.Wire @@ -52,7 +51,7 @@ public static ProtocolMessage Sign(this ProtocolMessage protocolMessage, } protocolMessage.Signature = null; - var signatureBytes = keySigner.Sign(protocolMessage.ToByteArray(), + var signatureBytes = keySigner.Sign(protocolMessage, signingContext).SignatureBytes; var signature = new Signature { diff --git a/src/Catalyst.Core.Lib/Extensions/Protocol/Wire/TransactionBroadcastExtensions.cs b/src/Catalyst.Core.Lib/Extensions/Protocol/Wire/PublicEntryExtensions.cs similarity index 62% rename from src/Catalyst.Core.Lib/Extensions/Protocol/Wire/TransactionBroadcastExtensions.cs rename to src/Catalyst.Core.Lib/Extensions/Protocol/Wire/PublicEntryExtensions.cs index fb66c6977b..c98aff38df 100644 --- a/src/Catalyst.Core.Lib/Extensions/Protocol/Wire/TransactionBroadcastExtensions.cs +++ b/src/Catalyst.Core.Lib/Extensions/Protocol/Wire/PublicEntryExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,49 +24,54 @@ using System.Reflection; using Catalyst.Abstractions.Cryptography; using Catalyst.Protocol.Cryptography; -using Catalyst.Protocol.Wire; +using Catalyst.Protocol.Transaction; using Google.Protobuf; -using Nethermind.Dirichlet.Numerics; using Serilog; namespace Catalyst.Core.Lib.Extensions.Protocol.Wire { - public static class TransactionBroadcastExtensions + public static class PublicEntryExtensions { private static readonly ILogger Logger = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType); - public static UInt256 SummedEntryFees(this TransactionBroadcast transaction) + public static Signature GenerateSignature(this PublicEntry publicEntry, + ICryptoContext cryptoContext, + IPrivateKey privateKey, + SigningContext context) { - var sum = transaction.ContractEntries.Sum(e => e.Base.TransactionFees.ToUInt256()) - + transaction.PublicEntries.Sum(e => e.Base.TransactionFees.ToUInt256()) - + transaction.ConfidentialEntries.Sum(e => e.Base.TransactionFees.ToUInt256()); - return sum; + return GeneratePublicEntrySignature(publicEntry.Clone(), cryptoContext, privateKey, context); } - public static TransactionBroadcast Sign(this TransactionBroadcast transaction, + public static PublicEntry Sign(this PublicEntry publicEntry, ICryptoContext cryptoContext, IPrivateKey privateKey, SigningContext context) { - var clone = transaction.Clone(); + var clone = publicEntry.Clone(); - if (transaction.Signature?.RawBytes.Length == cryptoContext.SignatureLength) + if (publicEntry.Signature?.RawBytes.Length == cryptoContext.SignatureLength) { Logger.Debug("The transaction was already signed, returning a clone."); return clone; } - clone.Signature = null; - var signatureBytes = cryptoContext.Sign(privateKey, clone.ToByteArray(), - context.ToByteArray()).SignatureBytes; + clone.Signature = GeneratePublicEntrySignature(clone, cryptoContext, privateKey, context); + return clone; + } - clone.Signature = new Signature + private static Signature GeneratePublicEntrySignature(PublicEntry publicEntry, + ICryptoContext cryptoContext, + IPrivateKey privateKey, + SigningContext context) + { + publicEntry.Signature = null; + var signatureBytes = cryptoContext.Sign(privateKey, publicEntry.ToByteArray(), + context.ToByteArray()).SignatureBytes; + return new Signature { RawBytes = signatureBytes.ToByteString(), SigningContext = context }; - - return clone; } } } diff --git a/src/Catalyst.Core.Lib/Extensions/RandomElementExtension.cs b/src/Catalyst.Core.Lib/Extensions/RandomElementExtension.cs index 06896d8c55..f5f7731f7f 100644 --- a/src/Catalyst.Core.Lib/Extensions/RandomElementExtension.cs +++ b/src/Catalyst.Core.Lib/Extensions/RandomElementExtension.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,7 +30,7 @@ namespace Catalyst.Core.Lib.Extensions { public static class RandomElementExtension { - private static readonly Random Rng = new Random(); + private static readonly Random Rng = new(); /// /// Takes a random sample from list, must have more than 3 items in list, to take a sample of at least 2 diff --git a/src/Catalyst.Core.Lib/Extensions/StreamExtensions.cs b/src/Catalyst.Core.Lib/Extensions/StreamExtensions.cs index 1038e7dc06..a1a9328dbc 100644 --- a/src/Catalyst.Core.Lib/Extensions/StreamExtensions.cs +++ b/src/Catalyst.Core.Lib/Extensions/StreamExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -37,7 +37,7 @@ public static string ReadAllAsUtf8String(this Stream stream, bool leaveOpen) stream.Seek(0, SeekOrigin.Begin); } - using (var reader = new StreamReader(stream, Encoding.UTF8, + using (StreamReader reader = new(stream, Encoding.UTF8, true, 4096, leaveOpen)) { return reader.ReadToEnd(); @@ -51,7 +51,7 @@ public static async Task ReadAllBytesAsync(this Stream stream, Cancellat stream.Seek(0, SeekOrigin.Begin); } - using (var memoryStream = new MemoryStream()) + await using (MemoryStream memoryStream = new()) { await stream.CopyToAsync(memoryStream, cancellationToken).ConfigureAwait(false); var contentBytes = memoryStream.ToArray(); diff --git a/src/Catalyst.Core.Lib/Extensions/StringExtensions.cs b/src/Catalyst.Core.Lib/Extensions/StringExtensions.cs index 117d1fd69e..661dcc49f5 100644 --- a/src/Catalyst.Core.Lib/Extensions/StringExtensions.cs +++ b/src/Catalyst.Core.Lib/Extensions/StringExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -35,8 +35,8 @@ public static class StringExtensions { public static Stream ToMemoryStream(this string s) { - var stream = new MemoryStream(); - var writer = new StreamWriter(stream); + MemoryStream stream = new(); + StreamWriter writer = new(stream); writer.Write(s); writer.Flush(); stream.Position = 0; @@ -45,21 +45,21 @@ public static Stream ToMemoryStream(this string s) public static byte[] ToUtf8Bytes(this string @string) { return Encoding.UTF8.GetBytes(@string); } - public static PeerId BuildPeerIdFromBase32Key(this string base32Key, IPEndPoint ipEndPoint) + public static PeerId BuildPeerIdFromBase58Key(this string base58Key, IPEndPoint ipEndPoint) { - return BuildPeerIdFromBase32Key(base32Key, ipEndPoint.Address, ipEndPoint.Port); + return BuildPeerIdFromBase58Key(base58Key, ipEndPoint.Address, ipEndPoint.Port); } - public static PeerId BuildPeerIdFromBase32Key(this string base32Key, + public static PeerId BuildPeerIdFromBase58Key(this string base58Key, IPAddress ipAddress, int port) { - return base32Key.KeyToBytes().BuildPeerIdFromPublicKey(ipAddress, port); + return base58Key.KeyToBytes().BuildPeerIdFromPublicKey(ipAddress, port); } public static T ParseHexStringTo(this string hex16Pid) where T : IMessage, new() { - var parser = new MessageParser(() => new T()); + MessageParser parser = new(() => new T()); return parser.ParseFrom(Base16.Decode(hex16Pid)); } } diff --git a/src/Catalyst.Core.Lib/FileSystem/FileStore.cs b/src/Catalyst.Core.Lib/FileSystem/FileStore.cs new file mode 100644 index 0000000000..bb6c019acd --- /dev/null +++ b/src/Catalyst.Core.Lib/FileSystem/FileStore.cs @@ -0,0 +1,357 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Nito.AsyncEx; + +namespace Catalyst.Core.Lib.FileSystem +{ + public interface IStore + { + Task TryGetAsync(TName name, CancellationToken cancel = default); + Task GetAsync(TName name, CancellationToken cancel = default); + Task PutAsync(TName name, TValue value, CancellationToken cancel = default); + Task RemoveAsync(TName name, CancellationToken cancel = default); + IEnumerable Values { get; } + } + + /// + /// A file based repository for name value pairs. + /// + /// + /// The type used for a unique name. + /// + /// + /// The type used for the value. + /// + /// + /// All operations are atomic, a reader/writer lock is used. + /// + public class FileStore : IStore + where TValue : class + { + private readonly AsyncReaderWriterLock _storeLock = new(); + + /// + /// A function to write the JSON encoded entity to the stream. + /// + /// + /// This is the default . + /// + public static Func JsonSerialize = + (stream, name, value, cancel) => + { + using StreamWriter writer = new(stream); + using var jtw = new JsonTextWriter(writer) + { + Formatting = Formatting.Indented + }; + JsonSerializer ser = new(); + ser.Serialize(jtw, value); + jtw.Flush(); + return Task.CompletedTask; + }; + + /// + /// A function to read the JSON encoded entity from the stream. + /// + /// + /// This is the default . + /// + public static Func> JsonDeserialize = + (stream, name, cancel) => + { + using StreamReader reader = new(stream); + using JsonTextReader jtr = new(reader); + JsonSerializer ser =new (); + return Task.FromResult(ser.Deserialize(jtr)); + }; + + /// + /// The fully qualififed path to a directory + /// that stores the name value pairs. + /// + /// + /// A fully qualified path. + /// + /// + /// The directory must already exist. + /// + public string Folder { get; set; } + + /// + /// A function that converts the name to a case insensitive key name. + /// + public Func NameToKey { get; set; } + + /// + /// A function that converts the case insensitive key to a name. + /// + public Func KeyToName { get; set; } + + /// + /// Sends the value to the stream. + /// + /// + /// Defaults to using . + /// + public Func Serialize { get; set; } = JsonSerialize; + + /// + /// Retrieves the value from the stream. + /// + /// + /// Defaults to using + /// + public Func> Deserialize { get; set; } = JsonDeserialize; + + /// + /// Try to get the value with the specified name. + /// + /// + /// The unique name of the entity. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a or null if the + /// does not exist. + /// + public async Task TryGetAsync(TName name, CancellationToken cancel = default) + { + var path = GetPath(name); + using (await _storeLock.ReaderLockAsync().ConfigureAwait(false)) + { + if (!File.Exists(path)) + { + return null; + } + + await using var content = File.OpenRead(path); + return await Deserialize(content, name, cancel).ConfigureAwait(false); + } + } + + /// + /// Get the value with the specified name. + /// + /// + /// The unique name of the entity. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a + /// + /// + /// When the does not exist. + /// + public async Task GetAsync(TName name, CancellationToken cancel = default) + { + var value = await TryGetAsync(name, cancel).ConfigureAwait(false); + if (value == null) + { + throw new KeyNotFoundException($"Missing '{name}'."); + } + + return value; + } + + /// + /// Put the value with the specified name. + /// + /// + /// The unique name of the entity. + /// + /// + /// The entity. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// If already exists, it's value is overwriten. + /// + /// The file is deleted if an exception is encountered. + /// + /// + public async Task PutAsync(TName name, TValue value, CancellationToken cancel = default) + { + var path = GetPath(name); + + using (await _storeLock.WriterLockAsync(cancel).ConfigureAwait(false)) + { + await using var stream = File.Create(path); + try + { + await Serialize(stream, name, value, cancel).ConfigureAwait(false); + } + catch (Exception) + { + try + { + stream?.DisposeAsync(); + File.Delete(path); + } + catch (Exception) + { + // eat it. + } + + throw; // original exception + } + } + } + + /// + /// Remove the value with the specified name. + /// + /// + /// The unique name of the entity. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// A non-existent does nothing. + /// + public async Task RemoveAsync(TName name, CancellationToken cancel = default) + { + var path = GetPath(name); + using (await _storeLock.WriterLockAsync(cancel).ConfigureAwait(false)) + { + File.Delete(path); + } + } + + /// + /// Get's the serialised length of the entity. + /// + /// + /// The unique name of the entity. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a nullable long. + /// + /// + /// Return a null when the does not exist. + /// + public async Task LengthAsync(TName name, CancellationToken cancel = default) + { + var path = GetPath(name); + + using (await _storeLock.ReaderLockAsync().ConfigureAwait(false)) + { + FileInfo fi = new(path); + long? length = null; + if (fi.Exists) + { + length = fi.Length; + } + + return length; + } + } + + /// + /// Determines if the name exists. + /// + /// + /// The unique name of the entity. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// true if the exists. + /// + public async Task ExistsAsync(TName name, CancellationToken cancel = default) + { + var path = GetPath(name); + using (await _storeLock.ReaderLockAsync().ConfigureAwait(false)) + { + return File.Exists(path); + } + } + + /// + /// Gets the values in the file store. + /// + /// + /// A sequence of . + /// + public IEnumerable Values + { + get + { + return Directory.EnumerateFiles(Folder) + .Select(path => + { + using var content = File.OpenRead(path); + var name = KeyToName(Path.GetFileName(path)); + return Deserialize(content, name, CancellationToken.None) + .ConfigureAwait(false) + .GetAwaiter() + .GetResult(); + }); + } + } + + /// + /// Gets the names in the file store. + /// + /// + /// A sequence of . + /// + public IEnumerable Names + { + get { return Directory.EnumerateFiles(Folder).Select(path => KeyToName(Path.GetFileName(path))); } + } + + /// + /// Local file system path to the name. + /// + public string GetPath(TName name) { return Path.Combine(Folder, NameToKey(name)); } + } +} diff --git a/src/Catalyst.Core.Lib/FileSystem/FileSystem.cs b/src/Catalyst.Core.Lib/FileSystem/FileSystem.cs index 0519691764..dc4a58bee5 100644 --- a/src/Catalyst.Core.Lib/FileSystem/FileSystem.cs +++ b/src/Catalyst.Core.Lib/FileSystem/FileSystem.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,12 +28,11 @@ using Catalyst.Core.Lib.Config; using Polly; using Polly.Retry; -using IFileSystem = Catalyst.Abstractions.FileSystem.IFileSystem; namespace Catalyst.Core.Lib.FileSystem { // ReSharper disable once ClassWithVirtualMembersNeverInherited.Global - public class FileSystem : System.IO.Abstractions.FileSystem, IFileSystem + public class FileSystem : System.IO.Abstractions.FileSystem, Catalyst.Abstractions.FileSystem.IFileSystem { // private readonly string _currentDataDirPointer; private string _dataDir; @@ -61,7 +60,7 @@ public bool SetCurrentPath(string path) { var fullPath = Path.GetFullPath(path); - var dirInfo = new DirectoryInfo(fullPath); + DirectoryInfo dirInfo = new(fullPath); if (!dirInfo.Exists) { dirInfo.Create(); @@ -100,7 +99,7 @@ private async Task WriteFileToPathAsync(string path, string contents) Directory.CreateDirectory(fileInfo.DirectoryName); } - using (var file = File.CreateText(path)) + await using (var file = File.CreateText(path)) { await file.WriteAsync(contents).ConfigureAwait(false); await file.FlushAsync().ConfigureAwait(false); @@ -118,8 +117,8 @@ public bool DataFileExistsInSubDirectory(string fileName, string subDirectory) { return File.Exists(Path.Combine(GetCatalystDataDir().FullName, subDirectory, fileName)); } - - private static string GetUserHomeDir() + + public static string GetUserHomeDir() { return Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); } diff --git a/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryClientAdded.cs b/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryClientAdded.cs index e181de94c6..e7d284873a 100644 --- a/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryClientAdded.cs +++ b/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryClientAdded.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryClientRemoved.cs b/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryClientRemoved.cs index 1287b34248..ef93f047d0 100644 --- a/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryClientRemoved.cs +++ b/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryClientRemoved.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryEvent.cs b/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryEvent.cs index 26fa598a33..a709a96e5f 100644 --- a/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryEvent.cs +++ b/src/Catalyst.Core.Lib/IO/Events/SocketClientRegistryEvent.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/IO/Events/TransactionReceivedEvent.cs b/src/Catalyst.Core.Lib/IO/Events/TransactionReceivedEvent.cs index 5c67d6c78c..966def3b45 100644 --- a/src/Catalyst.Core.Lib/IO/Events/TransactionReceivedEvent.cs +++ b/src/Catalyst.Core.Lib/IO/Events/TransactionReceivedEvent.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,14 +21,21 @@ #endregion +using System; +using Catalyst.Abstractions.Config; using Catalyst.Abstractions.IO.Events; using Catalyst.Abstractions.Mempool; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Validators; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Lib.Extensions; using Catalyst.Protocol.Rpc.Node; +using Catalyst.Protocol.Transaction; using Catalyst.Protocol.Wire; +using Google.Protobuf; +using MultiFormats; +using Nethermind.Core.Crypto; using Serilog; namespace Catalyst.Core.Lib.IO.Events @@ -37,47 +44,62 @@ public class TransactionReceivedEvent : ITransactionReceivedEvent { private readonly ITransactionValidator _validator; private readonly ILogger _logger; - private readonly IMempool _mempool; - private readonly IBroadcastManager _broadcastManager; + private readonly IMempool _mempool; + private readonly IPeerClient _peerClient; private readonly IMapperProvider _mapperProvider; public TransactionReceivedEvent(ITransactionValidator validator, - IMempool mempool, - IBroadcastManager broadcastManager, + IMempool mempool, + IPeerClient peerClient, IMapperProvider mapperProvider, ILogger logger) { _mapperProvider = mapperProvider; - _broadcastManager = broadcastManager; + _peerClient = peerClient; _mempool = mempool; _validator = validator; _logger = logger; } - public ResponseCode OnTransactionReceived(ProtocolMessage protocolMessage) + public ResponseCode OnTransactionReceived(ProtocolMessage protocolMessage, bool broadcast) { - var transaction = protocolMessage.FromProtocolMessage(); - var transactionValid = _validator.ValidateTransaction(transaction); - if (!transactionValid) + var transactionBroadcast = protocolMessage.FromProtocolMessage(); + PublicEntry publicEntry = transactionBroadcast.PublicEntry; + if (publicEntry.SenderAddress.Length == 32) { - return ResponseCode.Error; + var transactionValid = _validator.ValidateTransaction(publicEntry); + if (!transactionValid) + { + return ResponseCode.Error; + } + + byte[] kvmAddressBytes = Keccak.Compute(publicEntry.SenderAddress.ToByteArray()).Bytes.AsSpan(12).ToArray(); + string hex = kvmAddressBytes.ToHexString() ?? throw new ArgumentNullException("kvmAddressBytes.ToHexString()"); + publicEntry.SenderAddress = kvmAddressBytes.ToByteString(); + + if (publicEntry.ReceiverAddress.Length == 1) + { + publicEntry.ReceiverAddress = ByteString.Empty; + } } - var transactionBroadcastDao = transaction.ToDao(_mapperProvider); - var transactionSignature = transactionBroadcastDao.Signature; - _logger.Verbose("Adding transaction {signature} to mempool", transactionSignature); + var transactionDao = transactionBroadcast.PublicEntry.ToDao(_mapperProvider); + + _logger.Verbose("Adding transaction {id} to mempool", transactionDao.Id); - // https://github.com/catalyst-network/Catalyst.Node/issues/910 - should we fail or succeed if we already have the transaction in the ledger? - if (_mempool.Repository.TryReadItem(transactionSignature.RawBytes)) + if (_mempool.Service.TryReadItem(transactionDao.Id)) { - _logger.Information("Transaction {signature} already exists in mempool", transactionSignature); - return ResponseCode.Error; + _logger.Information("Transaction {id} already exists in mempool", transactionDao.Id); + return ResponseCode.Exists; } - _mempool.Repository.CreateItem(transactionBroadcastDao); + _mempool.Service.CreateItem(transactionDao); - _logger.Information("Broadcasting {signature} transaction", protocolMessage); - _broadcastManager.BroadcastAsync(protocolMessage).ConfigureAwait(false); + if (broadcast) + { + _logger.Information("Broadcasting {signature} transaction", protocolMessage); + _peerClient.BroadcastAsync(protocolMessage).ConfigureAwait(false); + } return ResponseCode.Successful; } diff --git a/src/Catalyst.Core.Lib/IO/Messaging/Correlation/CorrelatableMessage.cs b/src/Catalyst.Core.Lib/IO/Messaging/Correlation/CorrelatableMessage.cs index b3a7e34c93..61ce9382cb 100644 --- a/src/Catalyst.Core.Lib/IO/Messaging/Correlation/CorrelatableMessage.cs +++ b/src/Catalyst.Core.Lib/IO/Messaging/Correlation/CorrelatableMessage.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,13 +25,14 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Protocol.Peer; using Google.Protobuf; +using MultiFormats; namespace Catalyst.Core.Lib.IO.Messaging.Correlation { public sealed class CorrelatableMessage : ICorrelatableMessage where T : IMessage { public T Content { get; set; } - public PeerId Recipient { get; set; } + public MultiAddress Recipient { get; set; } public DateTimeOffset SentAt { get; set; } } } diff --git a/src/Catalyst.Core.Lib/IO/Messaging/Correlation/CorrelationId.cs b/src/Catalyst.Core.Lib/IO/Messaging/Correlation/CorrelationId.cs index 564a980274..9fd74f3cfe 100644 --- a/src/Catalyst.Core.Lib/IO/Messaging/Correlation/CorrelationId.cs +++ b/src/Catalyst.Core.Lib/IO/Messaging/Correlation/CorrelationId.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/IO/Messaging/Correlation/MessageCorrelationManagerBase.cs b/src/Catalyst.Core.Lib/IO/Messaging/Correlation/MessageCorrelationManagerBase.cs index b4ae288fad..aac0f3b0aa 100644 --- a/src/Catalyst.Core.Lib/IO/Messaging/Correlation/MessageCorrelationManagerBase.cs +++ b/src/Catalyst.Core.Lib/IO/Messaging/Correlation/MessageCorrelationManagerBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/IO/Messaging/Correlation/MessageEvictionEvent.cs b/src/Catalyst.Core.Lib/IO/Messaging/Correlation/MessageEvictionEvent.cs index 2d051f0aee..a10ff2c463 100644 --- a/src/Catalyst.Core.Lib/IO/Messaging/Correlation/MessageEvictionEvent.cs +++ b/src/Catalyst.Core.Lib/IO/Messaging/Correlation/MessageEvictionEvent.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,18 +24,19 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Protocol.Peer; using Google.Protobuf; +using MultiFormats; namespace Catalyst.Core.Lib.IO.Messaging.Correlation { public sealed class MessageEvictionEvent : ICacheEvictionEvent where T : IMessage { public T EvictedContent { get; } - public PeerId PeerId { get; } + public MultiAddress Address { get; } - public MessageEvictionEvent(CorrelatableMessage correlatableMessage, PeerId sender) + public MessageEvictionEvent(CorrelatableMessage correlatableMessage, MultiAddress sender) { EvictedContent = correlatableMessage.Content; - PeerId = sender; + Address = sender; } } } diff --git a/src/Catalyst.Core.Lib/IO/Observers/BroadcastObserverBase.cs b/src/Catalyst.Core.Lib/IO/Observers/BroadcastObserverBase.cs index 76a0e74acd..97a5cb1678 100644 --- a/src/Catalyst.Core.Lib/IO/Observers/BroadcastObserverBase.cs +++ b/src/Catalyst.Core.Lib/IO/Observers/BroadcastObserverBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,6 @@ #endregion using System; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Extensions; @@ -33,21 +32,23 @@ namespace Catalyst.Core.Lib.IO.Observers { - public abstract class BroadcastObserverBase : MessageObserverBase, IBroadcastObserver where TProto : IMessage + public abstract class BroadcastObserverBase : MessageObserverBase, IBroadcastObserver where TProto : IMessage { - protected BroadcastObserverBase(ILogger logger) : base(logger, typeof(TProto).ShortenedProtoFullName()) + private static Func FilterExpression = m => m?.TypeUrl != null && m.TypeUrl == typeof(TProto).ShortenedProtoFullName(); + + protected BroadcastObserverBase(ILogger logger) : base(logger, FilterExpression) { Guard.Argument(typeof(TProto), nameof(TProto)).Require(t => t.IsBroadcastType(), t => $"{nameof(TProto)} is not of type {MessageTypes.Broadcast.Name}"); } - public abstract void HandleBroadcast(IObserverDto messageDto); + public abstract void HandleBroadcast(ProtocolMessage message); - public override void OnNext(IObserverDto messageDto) + public override void OnNext(ProtocolMessage message) { try { - HandleBroadcast(messageDto); + HandleBroadcast(message); } catch (Exception exception) { diff --git a/src/Catalyst.Core.Lib/IO/Observers/MessageObserverBase.cs b/src/Catalyst.Core.Lib/IO/Observers/MessageObserverBase.cs index c54ff6e08b..ea98e8cd77 100644 --- a/src/Catalyst.Core.Lib/IO/Observers/MessageObserverBase.cs +++ b/src/Catalyst.Core.Lib/IO/Observers/MessageObserverBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,39 +23,34 @@ using System; using System.Reactive.Linq; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.IO.Observers; -using Catalyst.Protocol.Wire; using Serilog; namespace Catalyst.Core.Lib.IO.Observers { - public abstract class MessageObserverBase : IMessageObserver, IDisposable + public abstract class MessageObserverBase : IMessageObserver, IDisposable { protected readonly ILogger Logger; protected IDisposable MessageSubscription; - private readonly string _filterMessageType; + private readonly Func _filterExpression; - protected MessageObserverBase(ILogger logger, string filterMessageType) + protected MessageObserverBase(ILogger logger, Func filterExpression) { Logger = logger; - _filterMessageType = filterMessageType; + _filterExpression = filterExpression; } - public void StartObserving(IObservable> messageStream) + public void StartObserving(IObservable messageStream) { if (MessageSubscription != null) { return; } - MessageSubscription = messageStream - .Where(m => m.Payload?.TypeUrl != null - && m.Payload.TypeUrl == _filterMessageType) - .Subscribe(this); + MessageSubscription = messageStream.Where(_filterExpression).Subscribe(this); } - public abstract void OnNext(IObserverDto messageDto); + public abstract void OnNext(T message); public virtual void OnCompleted() { diff --git a/src/Catalyst.Core.Lib/IO/Observers/RequestObserverBase.cs b/src/Catalyst.Core.Lib/IO/Observers/RequestObserverBase.cs index 7e4612a3d5..b80efd1040 100644 --- a/src/Catalyst.Core.Lib/IO/Observers/RequestObserverBase.cs +++ b/src/Catalyst.Core.Lib/IO/Observers/RequestObserverBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,55 +23,52 @@ using System; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Dawn; -using DotNetty.Transport.Channels; using Google.Protobuf; +using MultiFormats; using Serilog; namespace Catalyst.Core.Lib.IO.Observers { - public abstract class RequestObserverBase : MessageObserverBase, IRequestMessageObserver + public abstract class RequestObserverBase : MessageObserverBase, IRequestMessageObserver where TProtoReq : IMessage where TProtoRes : IMessage { + private static Func FilterExpression = m => m?.TypeUrl != null && m.TypeUrl == typeof(TProtoReq).ShortenedProtoFullName(); + public IPeerSettings PeerSettings { get; } - protected RequestObserverBase(ILogger logger, IPeerSettings peerSettings) : base(logger, typeof(TProtoReq).ShortenedProtoFullName()) + private readonly IPeerClient _peerClient; + + protected RequestObserverBase(ILogger logger, IPeerSettings peerSettings, IPeerClient peerClient) : base(logger, FilterExpression) { Guard.Argument(typeof(TProtoReq), nameof(TProtoReq)).Require(t => t.IsRequestType(), t => $"{nameof(TProtoReq)} is not of type {MessageTypes.Request.Name}"); PeerSettings = peerSettings; - logger.Verbose("{interface} instantiated", nameof(IRequestMessageObserver)); + _peerClient = peerClient; + logger.Verbose("{interface} instantiated", nameof(IRequestMessageObserver)); } - protected abstract TProtoRes HandleRequest(TProtoReq messageDto, IChannelHandlerContext channelHandlerContext, PeerId senderPeerId, ICorrelationId correlationId); + protected abstract TProtoRes HandleRequest(TProtoReq message, MultiAddress sender, ICorrelationId correlationId); - public override void OnNext(IObserverDto messageDto) + public override void OnNext(ProtocolMessage message) { Logger.Verbose("Pre Handle Message Called"); try { - var correlationId = messageDto.Payload.CorrelationId.ToCorrelationId(); - var recipientPeerId = messageDto.Payload.PeerId; + var correlationId = message.CorrelationId.ToCorrelationId(); + MultiAddress recipientAddress = new(message.Address); - var response = HandleRequest(messageDto.Payload.FromProtocolMessage(), - messageDto.Context, - recipientPeerId, + var response = HandleRequest(message.FromProtocolMessage(), + recipientAddress, correlationId); - var responseDto = new MessageDto( - response.ToProtocolMessage(PeerSettings.PeerId, correlationId), - recipientPeerId); - - messageDto.Context.Channel?.WriteAndFlushAsync(responseDto).ConfigureAwait(false); + _peerClient.SendMessageAsync(response.ToProtocolMessage(PeerSettings.Address, correlationId), recipientAddress); } catch (Exception exception) { diff --git a/src/Catalyst.Core.Lib/IO/Observers/ResponseObserverBase.cs b/src/Catalyst.Core.Lib/IO/Observers/ResponseObserverBase.cs index 8233d46201..86fa509d1c 100644 --- a/src/Catalyst.Core.Lib/IO/Observers/ResponseObserverBase.cs +++ b/src/Catalyst.Core.Lib/IO/Observers/ResponseObserverBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,22 +23,22 @@ using System; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Extensions; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Dawn; -using DotNetty.Transport.Channels; using Google.Protobuf; +using MultiFormats; using Serilog; namespace Catalyst.Core.Lib.IO.Observers { - public abstract class ResponseObserverBase : MessageObserverBase, IResponseMessageObserver where TProto : IMessage + public abstract class ResponseObserverBase : MessageObserverBase, IResponseMessageObserver where TProto : IMessage { - protected ResponseObserverBase(ILogger logger, bool assertMessageNameCheck = true) : base(logger, typeof(TProto).ShortenedProtoFullName()) + private static Func FilterExpression = m => m?.TypeUrl != null && m.TypeUrl == typeof(TProto).ShortenedProtoFullName(); + + protected ResponseObserverBase(ILogger logger, bool assertMessageNameCheck = true) : base(logger, FilterExpression) { if (assertMessageNameCheck) { @@ -47,15 +47,14 @@ protected ResponseObserverBase(ILogger logger, bool assertMessageNameCheck = tru } } - protected abstract void HandleResponse(TProto messageDto, IChannelHandlerContext channelHandlerContext, PeerId senderPeerId, ICorrelationId correlationId); + protected abstract void HandleResponse(TProto message, MultiAddress sender, ICorrelationId correlationId); - public override void OnNext(IObserverDto messageDto) + public override void OnNext(ProtocolMessage message) { Logger.Verbose("Pre Handle Message Called"); try { - HandleResponse(messageDto.Payload.FromProtocolMessage(), messageDto.Context, - messageDto.Payload.PeerId, messageDto.Payload.CorrelationId.ToCorrelationId()); + HandleResponse(message.FromProtocolMessage(), message.Address, message.CorrelationId.ToCorrelationId()); } catch (Exception exception) { diff --git a/src/Catalyst.Core.Lib/IO/SlicedStream.cs b/src/Catalyst.Core.Lib/IO/SlicedStream.cs new file mode 100644 index 0000000000..0d7e763091 --- /dev/null +++ b/src/Catalyst.Core.Lib/IO/SlicedStream.cs @@ -0,0 +1,109 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Catalyst.Core.Lib.IO +{ + /// + /// Provides read only access to a slice of stream. + /// + public sealed class SlicedStream : Stream + { + private readonly Stream _stream; + private readonly long _offset; + private readonly long _logicalEnd; + + public SlicedStream(Stream stream, long offset, long count) + { + this._stream = stream; + this._offset = offset; + + stream.Position = offset; + _logicalEnd = count < 1 + ? stream.Length + : Math.Min(stream.Length, offset + count); + } + + public override bool CanRead => _stream.CanRead; + + public override bool CanSeek => false; + + public override bool CanWrite => false; + + public override long Length => _stream.Length; + + public override bool CanTimeout => _stream.CanTimeout; + + public override int ReadTimeout { get => _stream.ReadTimeout; set => _stream.ReadTimeout = value; } + + public override int WriteTimeout { get => _stream.WriteTimeout; set => _stream.WriteTimeout = value; } + + public override void Flush() { _stream.Flush(); } + + public override long Position { get => _stream.Position - _offset; set => throw new NotSupportedException(); } + + public override int ReadByte() + { + if (_stream.Position >= _logicalEnd) + { + return -1; + } + + return _stream.ReadByte(); + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (_stream.Position >= _logicalEnd) + { + return 0; + } + + var length = Math.Min(count, _logicalEnd - _stream.Position); + var n = _stream.Read(buffer, offset, (int) length); + return n; + } + + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + + public override void SetLength(long value) { throw new NotSupportedException(); } + + public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + + public override Task FlushAsync(CancellationToken cancellationToken) + { + return _stream.FlushAsync(cancellationToken); + } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + throw new NotSupportedException(); + } + + public override void WriteByte(byte value) { throw new NotSupportedException(); } + } +} diff --git a/src/Catalyst.Core.Lib/JsonConfiguredModule.cs b/src/Catalyst.Core.Lib/JsonConfiguredModule.cs index 3f9e662656..073d4e6181 100644 --- a/src/Catalyst.Core.Lib/JsonConfiguredModule.cs +++ b/src/Catalyst.Core.Lib/JsonConfiguredModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Kernel/CatalystModule.cs b/src/Catalyst.Core.Lib/Kernel/CatalystModule.cs new file mode 100644 index 0000000000..81fdca48fb --- /dev/null +++ b/src/Catalyst.Core.Lib/Kernel/CatalystModule.cs @@ -0,0 +1,45 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Autofac; + +namespace Catalyst.Core.Lib.Kernel +{ + public abstract class CatalystModule : Module + { + protected abstract void LoadApi(ContainerBuilder builder); + protected abstract void LoadService(ContainerBuilder builder); + protected abstract void LoadOptions(ContainerBuilder builder); + + /// + /// base load method + /// + /// + protected override void Load(ContainerBuilder builder) + { + LoadApi(builder); + LoadService(builder); + LoadOptions(builder); + } + } +} diff --git a/src/Catalyst.Core.Lib/Kernel/Kernel.cs b/src/Catalyst.Core.Lib/Kernel/Kernel.cs index a889391f1d..395ef837ec 100644 --- a/src/Catalyst.Core.Lib/Kernel/Kernel.cs +++ b/src/Catalyst.Core.Lib/Kernel/Kernel.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -31,13 +31,17 @@ using AutofacSerilogIntegration; using Catalyst.Abstractions.Config; using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Ledger.Models; using Catalyst.Abstractions.Types; using Catalyst.Abstractions.Util; using Catalyst.Core.Lib.Config; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Lib.Util; using Catalyst.Protocol.Network; using Microsoft.Extensions.Configuration; using Serilog; +using Serilog.Filters; using SharpRepository.Ioc.Autofac; using SharpRepository.Repository; @@ -52,6 +56,7 @@ public sealed class Kernel : IDisposable private IConfigCopier _configCopier; private readonly ConfigurationBuilder _configurationBuilder; public ILifetimeScope Instance; + private NetworkType _networkType; public ILogger Logger { get; private set; } public ContainerBuilder ContainerBuilder { get; set; } @@ -81,10 +86,11 @@ private Kernel(ICancellationTokenProvider cancellationTokenProvider, string file Logger = new LoggerConfiguration() .WriteTo.Console() .WriteTo.File(Path.Combine(Path.GetTempPath(), fileName), rollingInterval: RollingInterval.Day) + .Filter.ByExcluding(Matching.FromSource("Microsoft")) .CreateLogger() .ForContext(MethodBase.GetCurrentMethod().DeclaringType); - CancellationTokenProvider = cancellationTokenProvider ?? new CancellationTokenProvider(); + CancellationTokenProvider = cancellationTokenProvider ?? new CancellationTokenProvider(true); _fileName = fileName; ContainerBuilder = new ContainerBuilder(); @@ -94,12 +100,15 @@ private Kernel(ICancellationTokenProvider cancellationTokenProvider, string file public Kernel BuildKernel(bool overwrite = false, string overrideNetworkFile = null) { _overwrite = overwrite; - _configCopier.RunConfigStartUp(_targetConfigFolder, NetworkType.Devnet, null, _overwrite, overrideNetworkFile); + _configCopier.RunConfigStartUp(_targetConfigFolder, _networkType, null, _overwrite, overrideNetworkFile); var config = _configurationBuilder.Build(); - var configurationModule = new ConfigurationModule(config); + ConfigurationModule configurationModule = new(config); ContainerBuilder.RegisterInstance(config); + ContainerBuilder.RegisterType().As() + .WithParameter("networkType", _networkType).SingleInstance(); + ContainerBuilder.RegisterModule(configurationModule); if (!string.IsNullOrEmpty(_withPersistence)) @@ -114,6 +123,8 @@ public Kernel BuildKernel(bool overwrite = false, string overrideNetworkFile = n .File(Path.Combine(_targetConfigFolder, _fileName), rollingInterval: RollingInterval.Day, outputTemplate: "{Timestamp:HH:mm:ss} [{Level:u3}] ({MachineName}/{ThreadId}) {Message} ({SourceContext}){NewLine}{Exception}") + .Filter.ByExcluding(Matching.FromSource("Microsoft")) + .Filter.ByExcluding(Matching.FromSource("LibP2P")) .CreateLogger() .ForContext(MethodBase.GetCurrentMethod().DeclaringType); ContainerBuilder.RegisterLogger(Logger); @@ -129,9 +140,9 @@ public Kernel WithDataDirectory() return this; } - public Kernel WithNetworksConfigFile(NetworkType networkType = NetworkType.Devnet, string overrideNetworkFile = null) + public Kernel WithNetworksConfigFile(string overrideNetworkFile = null) { - var fileName = Constants.NetworkConfigFile(networkType, overrideNetworkFile); + var fileName = Constants.NetworkConfigFile(_networkType, overrideNetworkFile); _configurationBuilder .AddJsonFile( @@ -141,6 +152,19 @@ public Kernel WithNetworksConfigFile(NetworkType networkType = NetworkType.Devne return this; } + public Kernel WithValidatorSetFile() + { + var fileName = Constants.ValidatorSetConfigFile; + + _configurationBuilder + .AddJsonFile( + Path.Combine(_targetConfigFolder, fileName) + ); + + return this; + } + + public Kernel WithSerilogConfigFile(string serilog = default) { _configurationBuilder @@ -161,6 +185,13 @@ public Kernel WithConfigurationFile(string configFileName) return this; } + //Default network type set + public Kernel WithNetworkType(NetworkType networkType) + { + _networkType = networkType!=NetworkType.Unknown ? networkType : NetworkType.Testnet; + return this; + } + public Kernel WithConfigCopier(IConfigCopier configCopier) { _configCopier = configCopier; @@ -198,7 +229,7 @@ public Kernel WithPassword(PasswordRegistryTypes types, string password) ContainerBuilder.RegisterBuildCallback(buildCallback => { var passwordRegistry = buildCallback.Resolve(); - var ss = new SecureString(); + SecureString ss = new(); foreach (var c in password) { ss.AppendChar(c); @@ -219,5 +250,87 @@ public void StartContainer() .BeginLifetimeScope(declaringType.AssemblyQualifiedName); } } + + public Kernel Reset(bool shouldReset) + { + if (shouldReset) + { + Logger.Information("Resetting State"); + + var stateFolder = Path.Join(_targetConfigFolder, "state"); + if (Directory.Exists(stateFolder)) + { + Logger.Information("Deleting EVM State"); + Directory.Delete(stateFolder, true); + } + + var codeFolder = Path.Join(_targetConfigFolder, "code"); + if (Directory.Exists(codeFolder)) + { + Logger.Information("Deleting EVM Code"); + Directory.Delete(codeFolder, true); + } + + var blockFolder = Path.Join(_targetConfigFolder, "dfs", "blocks"); + if (Directory.Exists(blockFolder)) + { + Logger.Information("Deleting DFS Blocks"); + Directory.Delete(blockFolder, true); + } + + var pinFolder = Path.Join(_targetConfigFolder, "dfs", "pins"); + if (Directory.Exists(pinFolder)) + { + Logger.Information("Deleting DFS Pins"); + Directory.Delete(pinFolder, true); + } + + ContainerBuilder.RegisterBuildCallback(buildCallback => + { + var deltaIndexes = buildCallback.Resolve>(); + var transactionReceipts = buildCallback.Resolve>(); + var transactionToDeltas = buildCallback.Resolve>(); + var mempool = buildCallback.Resolve>(); + + Logger.Information("Deleting DeltaIndexes"); + foreach (var deltaIndex in deltaIndexes.GetAll()) + { + deltaIndexes.Delete(deltaIndex); + } + + Logger.Information("Deleting transactionReceipts"); + foreach (var transactionReceipt in transactionReceipts.GetAll()) + { + transactionReceipts.Delete(transactionReceipt); + } + + Logger.Information("Deleting transactionToDeltas"); + foreach (var transactionToDelta in transactionToDeltas.GetAll()) + { + transactionToDeltas.Delete(transactionToDelta); + } + + Logger.Information("Deleting mempool"); + foreach (var mempoolItem in mempool.GetAll()) + { + mempool.Delete(mempoolItem); + } + }); + } + + return this; + } + + public Kernel Uninstall(bool shouldUninstall) + { + if (shouldUninstall) + { + Logger.Information("Uninstalling Catalyst Node"); + + new FileSystem.FileSystem().GetCatalystDataDir().Delete(true); + } + + return this; + } } } diff --git a/src/Catalyst.Core.Lib/Network/DevDnsClient.cs b/src/Catalyst.Core.Lib/Network/DevDnsClient.cs index 2031ac2276..c1bb7e06d0 100644 --- a/src/Catalyst.Core.Lib/Network/DevDnsClient.cs +++ b/src/Catalyst.Core.Lib/Network/DevDnsClient.cs @@ -2,7 +2,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -33,6 +33,7 @@ using Catalyst.Protocol.Peer; using DnsClient; using Google.Protobuf; +using MultiFormats; using SimpleBase; namespace Catalyst.Core.Lib.Network @@ -41,15 +42,16 @@ public sealed class DevDnsClient : IDns { private readonly IList _seedServers; private readonly IList _dnsQueryAnswerValues; - private readonly IEnumerable _peerIds; + private readonly IEnumerable _peerIds; public DevDnsClient(IPeerSettings peerSettings) { _seedServers = peerSettings.SeedServers; _peerIds = Enumerable.Range(0, 5) - .Select(i => ByteUtil.GenerateRandomByteArray(32).BuildPeerIdFromPublicKey(IPAddress.Any, 1234)); - var peerIdsAsStrings = _peerIds.Select(p => Base16.EncodeLower(p.ToByteArray())); + .Select(i => new MultiAddress($"/ip4/192.168.0.181/tcp/{4000 + i}/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4")); + + var peerIdsAsStrings = _peerIds.Select(p => p.ToString()); _dnsQueryAnswerValues = peerIdsAsStrings.ToArray(); } @@ -69,10 +71,10 @@ public async Task GetTxtRecordsAsync(string hostname = "seed1 }; return await Task.FromResult(devDnsQueryResponse).ConfigureAwait(false); } - + /// #pragma warning disable 1998 - public async Task> GetSeedNodesFromDnsAsync(IEnumerable seedServers) + public async Task> GetSeedNodesFromDnsAsync(IEnumerable seedServers) #pragma warning restore 1998 { return _peerIds; diff --git a/src/Catalyst.Core.Lib/Network/DevDnsQueryResponse.cs b/src/Catalyst.Core.Lib/Network/DevDnsQueryResponse.cs index dc8084d006..954ce60fb9 100644 --- a/src/Catalyst.Core.Lib/Network/DevDnsQueryResponse.cs +++ b/src/Catalyst.Core.Lib/Network/DevDnsQueryResponse.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Network/DnsClient.cs b/src/Catalyst.Core.Lib/Network/DnsClient.cs index 4c77ffde97..b978725483 100644 --- a/src/Catalyst.Core.Lib/Network/DnsClient.cs +++ b/src/Catalyst.Core.Lib/Network/DnsClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -31,6 +31,7 @@ using Dawn; using DnsClient; using DnsClient.Protocol; +using MultiFormats; namespace Catalyst.Core.Lib.Network { @@ -74,9 +75,9 @@ public async Task GetTxtRecordsAsync(string hostname) /// /// /// - public async Task> GetSeedNodesFromDnsAsync(IEnumerable seedServers) + public async Task> GetSeedNodesFromDnsAsync(IEnumerable seedServers) { - var peers = new List(); + List peers = new(); async Task Action(string seedServer) { @@ -85,7 +86,7 @@ async Task Action(string seedServer) Guard.Argument(answerSection?.EscapedText).NotNull().Count(1); answerSection?.EscapedText.ToList() - .ForEach(stringPid => peers.Add(stringPid.ParseHexStringTo())); + .ForEach(stringPid => peers.Add(new MultiAddress(stringPid))); Guard.Argument(peers).MinCount(1); } diff --git a/src/Catalyst.Core.Lib/Network/EndpointBuilder.cs b/src/Catalyst.Core.Lib/Network/EndpointBuilder.cs index c7e77519aa..8c0ff3ccd3 100644 --- a/src/Catalyst.Core.Lib/Network/EndpointBuilder.cs +++ b/src/Catalyst.Core.Lib/Network/EndpointBuilder.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Network/InjectableLookupClient.cs b/src/Catalyst.Core.Lib/Network/InjectableLookupClient.cs index f04f0c499f..1fcc63dc52 100644 --- a/src/Catalyst.Core.Lib/Network/InjectableLookupClient.cs +++ b/src/Catalyst.Core.Lib/Network/InjectableLookupClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Network/Ip.cs b/src/Catalyst.Core.Lib/Network/Ip.cs index 0cfa97168b..4e54378dab 100644 --- a/src/Catalyst.Core.Lib/Network/Ip.cs +++ b/src/Catalyst.Core.Lib/Network/Ip.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/P2P/Discovery/HealthChecker.cs b/src/Catalyst.Core.Lib/P2P/Discovery/HealthChecker.cs index 78ad94f14e..4ff1d80a67 100644 --- a/src/Catalyst.Core.Lib/P2P/Discovery/HealthChecker.cs +++ b/src/Catalyst.Core.Lib/P2P/Discovery/HealthChecker.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -35,6 +35,7 @@ public class HealthChecker : IHealthChecker public void Run() { + // check if peer.cs IsAwol _subscription = Observable .Interval(TimeSpan.FromSeconds(10)) .StartWith(-1L) diff --git a/src/Catalyst.Core.Lib/P2P/Discovery/Neighbour.cs b/src/Catalyst.Core.Lib/P2P/Discovery/Neighbour.cs index 05bf02e133..b080927f63 100644 --- a/src/Catalyst.Core.Lib/P2P/Discovery/Neighbour.cs +++ b/src/Catalyst.Core.Lib/P2P/Discovery/Neighbour.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,20 +25,21 @@ using Catalyst.Abstractions.P2P.Discovery; using Catalyst.Abstractions.Types; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Core.Lib.P2P.Discovery { public sealed class Neighbour : INeighbour { public NeighbourStateTypes StateTypes { get; set; } - public PeerId PeerId { get; } + public MultiAddress Address { get; } public ICorrelationId DiscoveryPingCorrelationId { get; } - public Neighbour(PeerId peerId, + public Neighbour(MultiAddress address, NeighbourStateTypes stateTypes = default, ICorrelationId discoveryPingCorrelationId = default) { - PeerId = peerId; + Address = address; StateTypes = stateTypes ?? NeighbourStateTypes.NotContacted; DiscoveryPingCorrelationId = discoveryPingCorrelationId; } diff --git a/src/Catalyst.Core.Lib/P2P/Discovery/Neighbours.cs b/src/Catalyst.Core.Lib/P2P/Discovery/Neighbours.cs index c809b1a14c..e1d83e43e6 100644 --- a/src/Catalyst.Core.Lib/P2P/Discovery/Neighbours.cs +++ b/src/Catalyst.Core.Lib/P2P/Discovery/Neighbours.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -36,7 +36,7 @@ public override string ToString() { return string.Join(", ", this.Select(n => - n.PeerId + "|" + n.DiscoveryPingCorrelationId + "|" + n.StateTypes.Name)); + n.Address + "|" + n.DiscoveryPingCorrelationId + "|" + n.StateTypes.Name)); } } } diff --git a/src/Catalyst.Core.Lib/P2P/IO/Messaging/Correlation/PeerMessageCorrelationManager.cs b/src/Catalyst.Core.Lib/P2P/IO/Messaging/Correlation/PeerMessageCorrelationManager.cs index 118183e5b2..a951f1d7b7 100644 --- a/src/Catalyst.Core.Lib/P2P/IO/Messaging/Correlation/PeerMessageCorrelationManager.cs +++ b/src/Catalyst.Core.Lib/P2P/IO/Messaging/Correlation/PeerMessageCorrelationManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -38,6 +38,7 @@ using Catalyst.Protocol.Wire; using Dawn; using Microsoft.Extensions.Caching.Memory; +using MultiFormats; using Serilog; using IMemoryCache = Microsoft.Extensions.Caching.Memory.IMemoryCache; @@ -45,7 +46,7 @@ namespace Catalyst.Core.Lib.P2P.IO.Messaging.Correlation { public sealed class PeerMessageCorrelationManager : MessageCorrelationManagerBase, IPeerMessageCorrelationManager { - private readonly ReplaySubject> _evictionEvent; + private readonly ReplaySubject> _evictionEvent; private readonly ReplaySubject _reputationEvent; public PeerMessageCorrelationManager(IReputationManager reputationManager, @@ -57,14 +58,14 @@ public PeerMessageCorrelationManager(IReputationManager reputationManager, var streamScheduler = scheduler ?? Scheduler.Default; _reputationEvent = new ReplaySubject(0, streamScheduler); ReputationEventStream = _reputationEvent.AsObservable(); - _evictionEvent = new ReplaySubject>(0, streamScheduler); + _evictionEvent = new ReplaySubject>(0, streamScheduler); EvictionEventStream = _evictionEvent.AsObservable(); reputationManager.MergeReputationStream(ReputationEventStream); } public IObservable ReputationEventStream { get; } - public IObservable> EvictionEventStream { get; } + public IObservable> EvictionEventStream { get; } /// /// Takes a generic request type of IMessage, and generic response type of IMessage and the message and look them up in @@ -81,7 +82,7 @@ public override bool TryMatchResponse(ProtocolMessage response) { Logger.Debug($"{response.CorrelationId} message not found"); - _reputationEvent.OnNext(new ReputationChange(response.PeerId, + _reputationEvent.OnNext(new ReputationChange(new MultiAddress(response.Address), ReputationEventType.UnCorrelatableMessage) ); return false; @@ -90,7 +91,7 @@ public override bool TryMatchResponse(ProtocolMessage response) ValidateResponseType(response, message); Logger.Debug($"{response.CorrelationId} message found"); - _reputationEvent.OnNext(new ReputationChange(message.Recipient, + _reputationEvent.OnNext(new ReputationChange(message.Recipient.GetKvmAddress(), ReputationEventType.ResponseReceived) ); return true; @@ -108,11 +109,11 @@ protected override void EvictionCallback(object key, object value, EvictionReaso Logger.Verbose("{correlationId} message originally sent to {peer} is getting evicted", correlationId, message.Recipient); - _reputationEvent.OnNext(new ReputationChange(message.Content.PeerId, + _reputationEvent.OnNext(new ReputationChange(message.Content.Address, ReputationEventType.NoResponseReceived)); Logger.Verbose("PeerReputationChange sent for {correlationId}", correlationId); - _evictionEvent.OnNext(new KeyValuePair(correlationId, message.Recipient)); + _evictionEvent.OnNext(new KeyValuePair(correlationId, message.Recipient)); Logger.Verbose("EvictionEvent sent for {correlationId}", correlationId); } diff --git a/src/Catalyst.Core.Lib/P2P/IO/Messaging/Dto/PeerClientMessageDto.cs b/src/Catalyst.Core.Lib/P2P/IO/Messaging/Dto/PeerClientMessageDto.cs index e3f19d7ed1..f17aa684b0 100644 --- a/src/Catalyst.Core.Lib/P2P/IO/Messaging/Dto/PeerClientMessageDto.cs +++ b/src/Catalyst.Core.Lib/P2P/IO/Messaging/Dto/PeerClientMessageDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,22 +23,23 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.P2P.IO.Messaging.Dto; -using Catalyst.Protocol.Peer; using Dawn; using Google.Protobuf; +using MultiFormats; namespace Catalyst.Core.Lib.P2P.IO.Messaging.Dto { public sealed class PeerClientMessageDto : IPeerClientMessageDto { public ICorrelationId CorrelationId { get; set; } - public PeerId Sender { get; set; } + public MultiAddress Sender { get; set; } public IMessage Message { get; set; } - public PeerClientMessageDto(IMessage message, PeerId sender, ICorrelationId correlationId) + public PeerClientMessageDto(IMessage message, MultiAddress sender, ICorrelationId correlationId) { + var ns = message.GetType().Namespace; Guard.Argument(message, nameof(message)) - .Require(message.GetType().Namespace.Contains("IPPN")); + .Require(ns != null && ns.Contains("IPPN")); Message = message; Sender = sender; CorrelationId = correlationId; diff --git a/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHeightRequestObserver.cs b/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHeightRequestObserver.cs new file mode 100644 index 0000000000..46ad27c603 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHeightRequestObserver.cs @@ -0,0 +1,86 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.IO.Messaging.Correlation; +using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.P2P; +using Catalyst.Core.Abstractions.Sync; +using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Lib.IO.Observers; +using Catalyst.Core.Lib.Service; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.IPPN; +using Dawn; +using MultiFormats; +using Serilog; + +namespace Catalyst.Core.Lib.P2P.IO.Observers +{ + public sealed class DeltaHeightRequestObserver + : RequestObserverBase, + IP2PMessageObserver + { + private readonly IDeltaIndexService _deltaIndexService; + private readonly IMapperProvider _mapperProvider; + private readonly SyncState _syncState; + + public DeltaHeightRequestObserver(IPeerSettings peerSettings, + IDeltaIndexService deltaIndexService, + IMapperProvider mapperProvider, + IPeerClient peerClient, + SyncState syncState, + ILogger logger) + : base(logger, peerSettings, peerClient) + { + _deltaIndexService = deltaIndexService; + _mapperProvider = mapperProvider; + _syncState = syncState; + } + + /// + /// + /// + /// + /// + protected override LatestDeltaHashResponse HandleRequest(LatestDeltaHashRequest deltaHeightRequest, + MultiAddress sender, + ICorrelationId correlationId) + { + Guard.Argument(deltaHeightRequest, nameof(deltaHeightRequest)).NotNull(); + + Guard.Argument(sender, nameof(sender)).NotNull(); + + Logger.Debug("PeerId: {0} wants to know your current chain height", sender); + + var deltaIndexDao = _deltaIndexService.LatestDeltaIndex(); + var deltaIndex = DeltaIndexDao.ToProtoBuff(deltaIndexDao, _mapperProvider); + + return new LatestDeltaHashResponse + { + IsSync = _syncState.IsSynchronized, + DeltaIndex = deltaIndex + }; + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHeightResponseObserver.cs b/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHeightResponseObserver.cs new file mode 100644 index 0000000000..232418c567 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHeightResponseObserver.cs @@ -0,0 +1,68 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using Catalyst.Abstractions.IO.Messaging.Correlation; +using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.P2P.IO.Messaging.Dto; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Core.Lib.Abstractions.P2P.IO; +using Catalyst.Core.Lib.IO.Observers; +using Catalyst.Core.Lib.P2P.IO.Messaging.Dto; +using Catalyst.Core.Lib.P2P.Protocols; +using Catalyst.Protocol.IPPN; +using Lib.P2P; +using MultiFormats; +using Serilog; + +namespace Catalyst.Core.Lib.P2P.IO.Observers +{ + public sealed class DeltaHeightResponseObserver + : ResponseObserverBase, + IP2PMessageObserver, IPeerClientObservable + { + private readonly IPeerQueryTipRequest _peerQueryTipRequest; + public ReplaySubject ResponseMessageSubject { get; } + public IObservable MessageStream => ResponseMessageSubject.AsObservable(); + + public DeltaHeightResponseObserver(ILogger logger, IPeerQueryTipRequest peerQueryTipRequest) + : base(logger) + { + _peerQueryTipRequest = peerQueryTipRequest; + ResponseMessageSubject = new ReplaySubject(1); + } + + protected override void HandleResponse(LatestDeltaHashResponse deltaHeightResponse, + MultiAddress sender, + ICorrelationId correlationId) + { + ResponseMessageSubject.OnNext(new PeerClientMessageDto(deltaHeightResponse, sender, correlationId)); + + _peerQueryTipRequest.QueryTipResponseMessageStreamer.OnNext( + new PeerQueryTipResponse(sender, Cid.Read(deltaHeightResponse.DeltaIndex.Cid.ToByteArray())) + ); + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHistoryRequestObserver.cs b/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHistoryRequestObserver.cs new file mode 100644 index 0000000000..e012743107 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHistoryRequestObserver.cs @@ -0,0 +1,82 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using Catalyst.Abstractions.IO.Messaging.Correlation; +using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.P2P; +using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Lib.IO.Observers; +using Catalyst.Core.Lib.Service; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.IPPN; +using Dawn; +using MultiFormats; +using Serilog; + +namespace Catalyst.Core.Lib.P2P.IO.Observers +{ + public sealed class DeltaHistoryRequestObserver + : RequestObserverBase, + IP2PMessageObserver + { + private readonly IDeltaIndexService _deltaIndexService; + private readonly IMapperProvider _mapperProvider; + + public DeltaHistoryRequestObserver(IPeerSettings peerSettings, + IDeltaIndexService deltaIndexService, + IMapperProvider mapperProvider, + IPeerClient peerClient, + ILogger logger) + : base(logger, peerSettings, peerClient) + { + _deltaIndexService = deltaIndexService; + _mapperProvider = mapperProvider; + } + + /// + /// + /// + /// + /// + protected override DeltaHistoryResponse HandleRequest(DeltaHistoryRequest deltaHeightRequest, + MultiAddress sender, + ICorrelationId correlationId) + { + Guard.Argument(deltaHeightRequest, nameof(deltaHeightRequest)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); + + Logger.Debug("PeerId: {0} requests: {1} deltas from height: {2}", sender, deltaHeightRequest.Range, + deltaHeightRequest.Height); + + var rangeDao = _deltaIndexService.GetRange(deltaHeightRequest.Height, deltaHeightRequest.Range) + .ToList(); + var range = rangeDao.Select(x => DeltaIndexDao.ToProtoBuff(x, _mapperProvider)).ToList(); + + DeltaHistoryResponse response = new(); + response.DeltaIndex.Add(range); + return response; + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHistoryResponseObserver.cs b/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHistoryResponseObserver.cs new file mode 100644 index 0000000000..ade8596082 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/IO/Observers/DeltaHistoryResponseObserver.cs @@ -0,0 +1,65 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using Catalyst.Abstractions.IO.Messaging.Correlation; +using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.P2P.IO.Messaging.Dto; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Core.Lib.Abstractions.P2P.IO; +using Catalyst.Core.Lib.IO.Observers; +using Catalyst.Core.Lib.P2P.IO.Messaging.Dto; +using Catalyst.Protocol.IPPN; +using MultiFormats; +using Serilog; + +namespace Catalyst.Core.Lib.P2P.IO.Observers +{ + public sealed class DeltaHistoryResponseObserver + : ResponseObserverBase, + IP2PMessageObserver, IPeerClientObservable + { + private readonly IPeerDeltaHistoryRequest _deltaHistoryRequest; + public ReplaySubject ResponseMessageSubject { get; } + public IObservable MessageStream => ResponseMessageSubject.AsObservable(); + + public DeltaHistoryResponseObserver(ILogger logger, IPeerDeltaHistoryRequest deltaHistoryRequest) + : base(logger) + { + _deltaHistoryRequest = deltaHistoryRequest; + ResponseMessageSubject = new ReplaySubject(1); + } + + protected override void HandleResponse(DeltaHistoryResponse deltaHeightResponse, + MultiAddress sender, + ICorrelationId correlationId) + { + ResponseMessageSubject.OnNext(new PeerClientMessageDto(deltaHeightResponse, sender, correlationId)); + + //_deltaHistoryRequest.DeltaHistoryResponseMessageStreamer + // .OnNext(new PeerDeltaHistoryResponse(sender, deltaHeightResponse.Result)); + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/IO/Observers/GetNeighbourRequestObserver.cs b/src/Catalyst.Core.Lib/P2P/IO/Observers/GetNeighbourRequestObserver.cs index 4d221c5149..ce1f644b50 100644 --- a/src/Catalyst.Core.Lib/P2P/IO/Observers/GetNeighbourRequestObserver.cs +++ b/src/Catalyst.Core.Lib/P2P/IO/Observers/GetNeighbourRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,21 +21,17 @@ #endregion -using System.Linq; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; using Dawn; -using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; -using SharpRepository.Repository.Specifications; namespace Catalyst.Core.Lib.P2P.IO.Observers { @@ -47,9 +43,10 @@ public sealed class GetNeighbourRequestObserver public GetNeighbourRequestObserver(IPeerSettings peerSettings, IPeerRepository repository, + IPeerClient peerClient, ILogger logger) - : base(logger, peerSettings) - { + : base(logger, peerSettings, peerClient) + { _repository = repository; } @@ -58,32 +55,27 @@ public GetNeighbourRequestObserver(IPeerSettings peerSettings, /// /// /// - /// + /// /// /// protected override PeerNeighborsResponse HandleRequest(PeerNeighborsRequest peerNeighborsRequest, - IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(peerNeighborsRequest, nameof(peerNeighborsRequest)).NotNull(); - Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); - + Guard.Argument(sender, nameof(sender)).NotNull(); + Logger.Debug("PeerNeighborsRequest Message Received"); - var activePeersList = _repository - .FindAll(new Specification(p => !p.IsAwolPeer)) - .Take(Constants.NumberOfRandomPeers) // 😂 - .ToList(); - + var activePeersList = _repository.GetActivePeers(Constants.NumberOfRandomPeers); + Guard.Argument(activePeersList).MinCount(1); - var peerNeighborsResponseMessage = new PeerNeighborsResponse(); - + PeerNeighborsResponse peerNeighborsResponseMessage = new(); + for (var i = 0; i < Constants.NumberOfRandomPeers; i++) { - peerNeighborsResponseMessage.Peers.Add(activePeersList.RandomElement().PeerId); + peerNeighborsResponseMessage.Peers.Add(activePeersList.RandomElement().Address.ToString()); } return peerNeighborsResponseMessage; diff --git a/src/Catalyst.Core.Lib/P2P/IO/Observers/GetNeighbourResponseObserver.cs b/src/Catalyst.Core.Lib/P2P/IO/Observers/GetNeighbourResponseObserver.cs index 117a2b8887..19383ceb25 100644 --- a/src/Catalyst.Core.Lib/P2P/IO/Observers/GetNeighbourResponseObserver.cs +++ b/src/Catalyst.Core.Lib/P2P/IO/Observers/GetNeighbourResponseObserver.cs @@ -2,7 +2,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,11 +29,11 @@ using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P.IO; using Catalyst.Abstractions.P2P.IO.Messaging.Dto; +using Catalyst.Core.Lib.Abstractions.P2P.IO; using Catalyst.Core.Lib.IO.Observers; using Catalyst.Core.Lib.P2P.IO.Messaging.Dto; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; -using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Lib.P2P.IO.Observers @@ -56,14 +56,13 @@ public GetNeighbourResponseObserver(ILogger logger) : base(logger) /// /// /// - /// + /// /// protected override void HandleResponse(PeerNeighborsResponse messageDto, - IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { - ResponseMessageSubject.OnNext(new PeerClientMessageDto(messageDto, senderPeerId, correlationId)); + ResponseMessageSubject.OnNext(new PeerClientMessageDto(messageDto, sender, correlationId)); } } } diff --git a/src/Catalyst.Core.Lib/P2P/IO/Observers/PingRequestObserver.cs b/src/Catalyst.Core.Lib/P2P/IO/Observers/PingRequestObserver.cs index 268d5ef8a8..abb0d12181 100644 --- a/src/Catalyst.Core.Lib/P2P/IO/Observers/PingRequestObserver.cs +++ b/src/Catalyst.Core.Lib/P2P/IO/Observers/PingRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,11 +24,11 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.IO.Observers; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; using Dawn; -using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Lib.P2P.IO.Observers @@ -37,25 +37,26 @@ public sealed class PingRequestObserver : RequestObserverBase, IP2PMessageObserver { - public PingRequestObserver(IPeerSettings peerSettings, + public PingRequestObserver(IPeerSettings peerSettings, + IPeerClient peerClient, ILogger logger) - : base(logger, peerSettings) { } - + : base(logger, peerSettings, peerClient) { + } + /// - /// + /// Basic method to handle ping messages. /// /// /// - /// + /// /// - /// - protected override PingResponse HandleRequest(PingRequest pingRequest, IChannelHandlerContext channelHandlerContext, PeerId senderPeerId, ICorrelationId correlationId) + /// + protected override PingResponse HandleRequest(PingRequest pingRequest, + MultiAddress sender, + ICorrelationId correlationId) { Guard.Argument(pingRequest, nameof(pingRequest)).NotNull(); - Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); - - Logger.Debug("message content is {0} IP: {1} PeerId: {2}", pingRequest, senderPeerId.Ip, senderPeerId); + Guard.Argument(sender, nameof(sender)).NotNull(); return new PingResponse(); } diff --git a/src/Catalyst.Core.Lib/P2P/IO/Observers/PingResponseObserver.cs b/src/Catalyst.Core.Lib/P2P/IO/Observers/PingResponseObserver.cs index 46ebd97668..b2a05f76f7 100644 --- a/src/Catalyst.Core.Lib/P2P/IO/Observers/PingResponseObserver.cs +++ b/src/Catalyst.Core.Lib/P2P/IO/Observers/PingResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,40 +26,44 @@ using System.Reactive.Subjects; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.P2P.IO; using Catalyst.Abstractions.P2P.IO.Messaging.Dto; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Core.Lib.Abstractions.P2P.IO; using Catalyst.Core.Lib.IO.Observers; using Catalyst.Core.Lib.P2P.IO.Messaging.Dto; +using Catalyst.Core.Lib.P2P.Protocols; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; -using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Lib.P2P.IO.Observers { + /// + /// @TODO we inject IPeerChallengeRequest, this class probably shouldnt care about IPeerChallengeRequest, + /// IPeerChallengeRequest should consume ResponseMessageSubject, and filter for messages it is concerned with. + /// OnNext(new PeerChallengeResponse(sender)); would then instantiate PeerChallengeResponse where its consumed not here. + /// public sealed class PingResponseObserver : ResponseObserverBase, IP2PMessageObserver, IPeerClientObservable { - private readonly IPeerChallenger _peerChallenger; + private readonly IPeerChallengeRequest _peerChallengeRequest; public ReplaySubject ResponseMessageSubject { get; } public IObservable MessageStream => ResponseMessageSubject.AsObservable(); - - public PingResponseObserver(ILogger logger, IPeerChallenger peerChallenger) + + public PingResponseObserver(IPeerChallengeRequest peerChallengeRequest, ILogger logger) : base(logger) { - _peerChallenger = peerChallenger; + _peerChallengeRequest = peerChallengeRequest; ResponseMessageSubject = new ReplaySubject(1); } protected override void HandleResponse(PingResponse pingResponse, - IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { - ResponseMessageSubject.OnNext(new PeerClientMessageDto(pingResponse, senderPeerId, correlationId)); - _peerChallenger.ChallengeResponseMessageStreamer.OnNext(new PeerChallengerResponse(senderPeerId)); + ResponseMessageSubject.OnNext(new PeerClientMessageDto(pingResponse, sender, correlationId)); + _peerChallengeRequest.ChallengeResponseMessageStreamer.OnNext(new PeerChallengeResponse(sender)); } } } diff --git a/src/Catalyst.Core.Lib/P2P/IO/Observers/TransactionBroadcastObserver.cs b/src/Catalyst.Core.Lib/P2P/IO/Observers/TransactionBroadcastObserver.cs index b6e1885461..d4d20e2403 100644 --- a/src/Catalyst.Core.Lib/P2P/IO/Observers/TransactionBroadcastObserver.cs +++ b/src/Catalyst.Core.Lib/P2P/IO/Observers/TransactionBroadcastObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,6 @@ #endregion using Catalyst.Abstractions.IO.Events; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.IO.Observers; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Observers; @@ -43,14 +42,14 @@ public TransactionBroadcastObserver(ILogger logger, ITransactionReceivedEvent tr _transactionReceivedEvent = transactionReceivedEvent; } - public override void HandleBroadcast(IObserverDto messageDto) + public override void HandleBroadcast(ProtocolMessage message) { Logger.Debug("received broadcast"); - var deserialised = messageDto.Payload.FromProtocolMessage(); - _transactionReceivedEvent.OnTransactionReceived(messageDto.Payload); + var deserialised = message.FromProtocolMessage(); + _transactionReceivedEvent.OnTransactionReceived(message, false); - Logger.Debug("transaction signature is {0}", deserialised.Signature); + Logger.Debug("transaction signature is {0}", deserialised.PublicEntry.Signature); } } } diff --git a/src/Catalyst.Core.Lib/P2P/KatDhtService.cs b/src/Catalyst.Core.Lib/P2P/KatDhtService.cs new file mode 100644 index 0000000000..9ab3a33ff5 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/KatDhtService.cs @@ -0,0 +1,42 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Lib.P2P; +using Lib.P2P.Routing; + +namespace Catalyst.Core.Lib.P2P +{ + public sealed class KatDhtService : DhtService + { + /// + /// Takes an injectable swarm service + /// + /// + public KatDhtService(ISwarmService swarmService) + { + SwarmService = swarmService; + } + + public override string Name { get; } = "catalyst/kad"; + } +} diff --git a/src/Catalyst.Core.Lib/P2P/LocalPeer.cs b/src/Catalyst.Core.Lib/P2P/LocalPeer.cs new file mode 100644 index 0000000000..dbc8509fe0 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/LocalPeer.cs @@ -0,0 +1,50 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Options; +using System.Reflection; +using LibP2P = Lib.P2P; + +namespace Catalyst.Core.Lib.P2P +{ + public class LocalPeer : LibP2P.Peer + { + public LocalPeer(IPasswordRepeater passwordRepeater, IKeyApi keyApi, KeyChainOptions keyChainOptions) + { + passwordRepeater.PromptAndAddPasswordToRegistryAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + + var self = keyApi.GetKeyAsync("self").ConfigureAwait(false).GetAwaiter().GetResult() ?? + keyApi.CreateAsync("self", keyChainOptions.DefaultKeyType, 0).ConfigureAwait(false).GetAwaiter().GetResult(); + + this.Id = self.Id; + this.PublicKey = keyApi.GetDfsPublicKeyAsync("self").ConfigureAwait(false).GetAwaiter().GetResult(); + this.ProtocolVersion = "ipfs/0.1.0"; + + var version = typeof(IDfsService).GetTypeInfo().Assembly.GetName().Version; + this.AgentVersion = $"net-ipfs/{version.Major}.{version.Minor}.{version.Revision}"; + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/PeerIdValidator.cs b/src/Catalyst.Core.Lib/P2P/PeerIdValidator.cs index a7b340a900..1dee0ef4b7 100644 --- a/src/Catalyst.Core.Lib/P2P/PeerIdValidator.cs +++ b/src/Catalyst.Core.Lib/P2P/PeerIdValidator.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,8 +26,8 @@ using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.P2P; using Catalyst.Core.Lib.Network; -using Catalyst.Protocol.Peer; using Dawn; +using MultiFormats; namespace Catalyst.Core.Lib.P2P { @@ -44,13 +44,10 @@ public PeerIdValidator(ICryptoContext cryptoContext) } /// - public bool ValidatePeerIdFormat(PeerId peerId) + public bool ValidatePeerIdFormat(MultiAddress address) { - var publicKeyLength = _cryptoContext.PublicKeyLength; - Guard.Argument(peerId, nameof(peerId)).NotNull() - .Require(p => p.PublicKey.Length == publicKeyLength, p => $"PublicKey should be {publicKeyLength} bytes but was {p.PublicKey.Length}") - .Require(p => p.Ip.Length == 16 && ValidateIp(p.Ip.ToByteArray()), _ => "Ip should be 16 bytes") - .Require(p => ValidatePort(p.Port), _ => "Port should be between 1025 and 65535"); + Guard.Argument(address, nameof(address)).NotNull(); + Guard.Argument(address.HasPeerId, nameof(address.HasPeerId)).True("MultiAddress has no PeerId"); return true; } diff --git a/src/Catalyst.Core.Lib/P2P/PeerSettings.cs b/src/Catalyst.Core.Lib/P2P/PeerSettings.cs index 00ebbddb1e..82b8d303c2 100644 --- a/src/Catalyst.Core.Lib/P2P/PeerSettings.cs +++ b/src/Catalyst.Core.Lib/P2P/PeerSettings.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,13 +25,23 @@ using System.Collections.Generic; using System.Linq; using System.Net; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Reflection; +using Catalyst.Abstractions.Config; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Keystore; using Catalyst.Abstractions.P2P; +using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Network; using Catalyst.Protocol.Network; using Catalyst.Protocol.Peer; using Dawn; +using Lib.P2P; using Microsoft.Extensions.Configuration; +using MultiFormats; namespace Catalyst.Core.Lib.P2P { @@ -48,20 +58,68 @@ public sealed class PeerSettings : IPeerSettings public IPAddress BindAddress { get; } public IList SeedServers { get; } public IPEndPoint[] DnsServers { get; } - public PeerId PeerId { get; } + public MultiAddress Address { set; get; } + + public IEnumerable GetAddresses(MultiAddress address, Peer localPeer) + { + MultiAddress result = new($"{address}/ipfs/{localPeer.Id}"); + + // Get the actual IP address(es). + IList addresses = null; + var ips = NetworkInterface.GetAllNetworkInterfaces() + + // It appears that the loopback adapter is not UP on Ubuntu 14.04.5 LTS + .Where(nic => nic.OperationalStatus == OperationalStatus.Up + || nic.NetworkInterfaceType == NetworkInterfaceType.Loopback) + .SelectMany(nic => nic.GetIPProperties().UnicastAddresses); + if (result.ToString().StartsWith("/ip4/0.0.0.0/")) + { + addresses = ips + .Where(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork) + .Select(ip => new MultiAddress(result.ToString().Replace("0.0.0.0", ip.Address.ToString()))) + .ToList(); + } + else if (result.ToString().StartsWith("/ip6/::/")) + { + addresses = ips + .Where(ip => ip.Address.AddressFamily == AddressFamily.InterNetworkV6) + .Select(ip => { return new MultiAddress(result.ToString().Replace("::", ip.Address.ToString())); }) + .ToList(); + } + else + { + addresses = new[] { result }; + } + + if (!addresses.Any()) + { + var msg = "Cannot determine address(es) for " + result; + + foreach (var ip in ips) + { + msg += " nic-ip: " + ip.Address; + } + + throw new Exception(msg); + } + + return addresses; + } /// - /// Set attributes + /// Set the local nodes peer settings /// /// - public PeerSettings(IConfigurationRoot rootSection) + public PeerSettings(IConfigurationRoot rootSection, Peer localPeer, IConfigApi configApi, INetworkTypeProvider networkTypeProvider) { + _networkType = networkTypeProvider.NetworkType; Guard.Argument(rootSection, nameof(rootSection)).NotNull(); - + var section = rootSection.GetSection("CatalystNodeConfiguration").GetSection("Peer"); - Enum.TryParse(section.GetSection("Network").Value, out _networkType); - PublicKey = section.GetSection("PublicKey").Value; + var pksi = Convert.FromBase64String(localPeer.PublicKey); + PublicKey = pksi.GetPublicKeyBytesFromPeerId().ToBase58(); + Port = int.Parse(section.GetSection("Port").Value); PayoutAddress = section.GetSection("PayoutAddress").Value; BindAddress = IPAddress.Parse(section.GetSection("BindAddress").Value); @@ -69,7 +127,17 @@ public PeerSettings(IConfigurationRoot rootSection) DnsServers = section.GetSection("DnsServers") .GetChildren() .Select(p => EndpointBuilder.BuildNewEndPoint(p.Value)).ToArray(); - PeerId = PublicKey.BuildPeerIdFromBase32Key(BindAddress, Port); + + var publicIpAddress = IPAddress.Parse(section.GetSection("PublicIpAddress").Value); + + var json = configApi.GetAsync("Addresses.Swarm").ConfigureAwait(false).GetAwaiter().GetResult(); + List addresses = new(); + foreach (string a in json) + { + addresses.AddRange(GetAddresses(a, localPeer)); + } + + Address = addresses.First(); } } } diff --git a/src/Catalyst.Core.Lib/P2P/PeerChallenger.cs b/src/Catalyst.Core.Lib/P2P/Protocols/PeerChallengeRequest.cs similarity index 71% rename from src/Catalyst.Core.Lib/P2P/PeerChallenger.cs rename to src/Catalyst.Core.Lib/P2P/Protocols/PeerChallengeRequest.cs index 7ec5f0a299..517c4eee23 100644 --- a/src/Catalyst.Core.Lib/P2P/PeerChallenger.cs +++ b/src/Catalyst.Core.Lib/P2P/Protocols/PeerChallengeRequest.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,6 @@ #endregion using System; -using System.Linq; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reactive.Subjects; @@ -30,58 +29,58 @@ using System.Threading; using System.Threading.Tasks; using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Protocols; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; +using Catalyst.Core.Lib.Util; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; +using MultiFormats; using Serilog; -namespace Catalyst.Core.Lib.P2P +namespace Catalyst.Core.Lib.P2P.Protocols { - public sealed class PeerChallenger : IPeerChallenger, IDisposable + public sealed class PeerChallengeRequest : ProtocolRequestBase, IPeerChallengeRequest { private readonly ILogger _logger; - private readonly PeerId _senderIdentifier; + private readonly MultiAddress _senderIdentifier; private readonly IPeerClient _peerClient; - private readonly int _peerChallengeWaitTimeSeconds; + private readonly int _ttl; public ReplaySubject ChallengeResponseMessageStreamer { get; } - public PeerChallenger(ILogger logger, + public PeerChallengeRequest(ILogger logger, IPeerClient peerClient, IPeerSettings peerSettings, - int peerChallengeWaitTimeSeconds, + int ttl, IScheduler scheduler = null) + : base(logger, + peerSettings.Address, + new CancellationTokenProvider(ttl), + peerClient) { var observableScheduler = scheduler ?? Scheduler.Default; ChallengeResponseMessageStreamer = new ReplaySubject(1, observableScheduler); - _senderIdentifier = peerSettings.PeerId; + _senderIdentifier = peerSettings.Address; _logger = logger; _peerClient = peerClient; - _peerChallengeWaitTimeSeconds = peerChallengeWaitTimeSeconds; + _ttl = ttl; } - public async Task ChallengePeerAsync(PeerId recipientPeerId) + public async Task ChallengePeerAsync(MultiAddress recipientAddress) { try { var correlationId = CorrelationId.GenerateCorrelationId(); var protocolMessage = new PingRequest().ToProtocolMessage(_senderIdentifier, correlationId); - var messageDto = new MessageDto( - protocolMessage, - recipientPeerId - ); - _logger.Verbose($"Sending peer challenge request to IP: {recipientPeerId}"); - _peerClient.SendMessage(messageDto); + _logger.Verbose($"Sending peer challenge request to IP: {recipientAddress}"); + await _peerClient.SendMessageAsync(protocolMessage, recipientAddress).ConfigureAwait(false); + using (var cancellationTokenSource = - new CancellationTokenSource(TimeSpan.FromSeconds(_peerChallengeWaitTimeSeconds))) + new CancellationTokenSource(TimeSpan.FromSeconds(_ttl))) { await ChallengeResponseMessageStreamer - .FirstAsync(a => a != null - && a.PeerId.PublicKey.SequenceEqual(recipientPeerId.PublicKey) - && a.PeerId.Ip.SequenceEqual(recipientPeerId.Ip)) + .FirstAsync(a => a != null && a.Address == recipientAddress) .ToTask(cancellationTokenSource.Token) .ConfigureAwait(false); } diff --git a/src/Catalyst.Core.Lib/P2P/Protocols/PeerChallengeResponse.cs b/src/Catalyst.Core.Lib/P2P/Protocols/PeerChallengeResponse.cs new file mode 100644 index 0000000000..c1ef7bd6b1 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/Protocols/PeerChallengeResponse.cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.P2P.Protocols; +using MultiFormats; + +namespace Catalyst.Core.Lib.P2P.Protocols +{ + public sealed class PeerChallengeResponse : ProtocolResponseBase, IPeerChallengeResponse + { + public PeerChallengeResponse(MultiAddress address) : base(address) + { + // @TODO touch last seen of peers + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/Protocols/PeerDeltaHistoryRequest.cs b/src/Catalyst.Core.Lib/P2P/Protocols/PeerDeltaHistoryRequest.cs new file mode 100644 index 0000000000..7487dd11ce --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/Protocols/PeerDeltaHistoryRequest.cs @@ -0,0 +1,102 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Abstractions.Util; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Protocol.IPPN; +using MultiFormats; +using Serilog; + +namespace Catalyst.Core.Lib.P2P.Protocols +{ + public class PeerDeltaHistoryRequest : ProtocolRequestBase, IPeerDeltaHistoryRequest + { + public ReplaySubject DeltaHistoryResponseMessageStreamer { get; } + + public PeerDeltaHistoryRequest(ILogger logger, + IPeerClient peerClient, + IPeerSettings peerSettings, + ICancellationTokenProvider cancellationTokenProvider, + IScheduler observableScheduler = null) : base(logger, peerSettings.Address, cancellationTokenProvider, peerClient) + { + DeltaHistoryResponseMessageStreamer = new ReplaySubject(1, observableScheduler ?? Scheduler.Default); + } + + public async Task DeltaHistoryAsync(MultiAddress recipientAddress, uint height, uint range) + { + IPeerDeltaHistoryResponse history; + try + { + await PeerClient.SendMessageAsync( + new DeltaHistoryRequest + { + Range = range, + Height = height + }.ToProtocolMessage(Address, CorrelationId.GenerateCorrelationId()), + recipientAddress + ); + + using (CancellationTokenProvider.CancellationTokenSource) + { + history = await DeltaHistoryResponseMessageStreamer + .FirstAsync(a => a != null && a.Address == recipientAddress) + .ToTask(CancellationTokenProvider.CancellationTokenSource.Token) + .ConfigureAwait(false); + } + } + catch (OperationCanceledException) + { + return null; + } + catch (Exception e) + { + Logger.Error(e, nameof(DeltaHistoryAsync)); + return null; + } + + return history; + } + + public void Dispose() { Dispose(true); } + + private void Dispose(bool disposing) + { + Disposing = disposing; + if (!Disposing) + { + return; + } + + DeltaHistoryResponseMessageStreamer?.Dispose(); + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/Protocols/PeerDeltaHistoryResponse.cs b/src/Catalyst.Core.Lib/P2P/Protocols/PeerDeltaHistoryResponse.cs new file mode 100644 index 0000000000..bdcf7becb2 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/Protocols/PeerDeltaHistoryResponse.cs @@ -0,0 +1,50 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Peer; +using Dawn; +using MultiFormats; + +namespace Catalyst.Core.Lib.P2P.Protocols +{ + public sealed class PeerDeltaHistoryResponse : ProtocolResponseBase, IPeerDeltaHistoryResponse + { + public ICollection DeltaCid { get; } + + /// + /// @TODO look at side effects/ how to handle out of range index more detail + /// Since the protocol is over udp we need to make sure we dont fragment udp packets + /// Could build a buffer into the udp pipeline + /// + /// + /// + public PeerDeltaHistoryResponse(MultiAddress address, ICollection deltaCid) : base(address) + { + Guard.Argument(deltaCid.Count).InRange(1, 1024); + DeltaCid = deltaCid; + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/Protocols/PeerQueryTipRequestRequest.cs b/src/Catalyst.Core.Lib/P2P/Protocols/PeerQueryTipRequestRequest.cs new file mode 100644 index 0000000000..53d253fce4 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/Protocols/PeerQueryTipRequestRequest.cs @@ -0,0 +1,105 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Reactive.Threading.Tasks; +using System.Threading.Tasks; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Abstractions.Util; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Protocol.IPPN; +using MultiFormats; +using Serilog; + +namespace Catalyst.Core.Lib.P2P.Protocols +{ + public sealed class PeerQueryTipRequestRequest : ProtocolRequestBase, IPeerQueryTipRequest + { + public ReplaySubject QueryTipResponseMessageStreamer { get; } + + /// + /// Protocol for querying a peers current delta height. + /// + /// + /// + /// + /// + /// + public PeerQueryTipRequestRequest(ILogger logger, + IPeerClient peerClient, + IPeerSettings peerSettings, + ICancellationTokenProvider cancellationTokenProvider, + IScheduler scheduler = null) : base(logger, peerSettings.Address, cancellationTokenProvider, peerClient) + { + QueryTipResponseMessageStreamer = new ReplaySubject(1, scheduler ?? Scheduler.Default); + } + + public async Task QueryPeerTipAsync(MultiAddress recipientPeerId) + { + try + { + await PeerClient.SendMessageAsync( + new LatestDeltaHashRequest().ToProtocolMessage(Address, CorrelationId.GenerateCorrelationId()), + recipientPeerId + ); + + using (CancellationTokenProvider.CancellationTokenSource) + { + await QueryTipResponseMessageStreamer + .FirstAsync(a => a != null && a.Address == recipientPeerId) + .ToTask(CancellationTokenProvider.CancellationTokenSource.Token) + .ConfigureAwait(false); + } + } + catch (OperationCanceledException) + { + return false; + } + catch (Exception e) + { + Logger.Error(e, nameof(QueryPeerTipAsync)); + return false; + } + + return true; + } + + public void Dispose() { Dispose(true); } + + private void Dispose(bool disposing) + { + Disposing = disposing; + if (!Disposing) + { + return; + } + + QueryTipResponseMessageStreamer?.Dispose(); + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/Protocols/PeerQueryTipResponse.cs b/src/Catalyst.Core.Lib/P2P/Protocols/PeerQueryTipResponse.cs new file mode 100644 index 0000000000..9014fcfdf5 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/Protocols/PeerQueryTipResponse.cs @@ -0,0 +1,39 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.P2P.Protocols; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Core.Lib.P2P.Protocols +{ + public sealed class PeerQueryTipResponse : ProtocolResponseBase, IPeerQueryTipResponse + { + public Cid DeltaHash { get; } + + public PeerQueryTipResponse(MultiAddress address, Cid deltaHash) : base(address) + { + DeltaHash = deltaHash; + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/Protocols/ProtocolBase.cs b/src/Catalyst.Core.Lib/P2P/Protocols/ProtocolBase.cs new file mode 100644 index 0000000000..035807c29a --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/Protocols/ProtocolBase.cs @@ -0,0 +1,35 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.P2P.Protocols; +using MultiFormats; + +namespace Catalyst.Core.Lib.P2P.Protocols +{ + public class ProtocolBase : IProtocol + { + public MultiAddress Address { get; } + + protected ProtocolBase(MultiAddress address) { Address = address; } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/Protocols/ProtocolRequestBase.cs b/src/Catalyst.Core.Lib/P2P/Protocols/ProtocolRequestBase.cs new file mode 100644 index 0000000000..aea310e3f8 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/Protocols/ProtocolRequestBase.cs @@ -0,0 +1,51 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Abstractions.Util; +using Catalyst.Protocol.Peer; +using MultiFormats; +using Serilog; + +namespace Catalyst.Core.Lib.P2P.Protocols +{ + public class ProtocolRequestBase : ProtocolBase, IProtocolRequest + { + protected readonly ILogger Logger; + protected readonly ICancellationTokenProvider CancellationTokenProvider; + + public IPeerClient PeerClient { get; } + public bool Disposing { get; protected set; } + + protected ProtocolRequestBase(ILogger logger, + MultiAddress sender, + ICancellationTokenProvider cancellationTokenProvider, + IPeerClient peerClient) : base(sender) + { + Logger = logger; + CancellationTokenProvider = cancellationTokenProvider; + PeerClient = peerClient; + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/Protocols/ProtocolResponseBase.cs b/src/Catalyst.Core.Lib/P2P/Protocols/ProtocolResponseBase.cs new file mode 100644 index 0000000000..fbefe8e0e8 --- /dev/null +++ b/src/Catalyst.Core.Lib/P2P/Protocols/ProtocolResponseBase.cs @@ -0,0 +1,32 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using MultiFormats; + +namespace Catalyst.Core.Lib.P2P.Protocols +{ + public class ProtocolResponseBase : ProtocolBase + { + protected ProtocolResponseBase(MultiAddress address) : base(address) { } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/Repository/IPeerRepository.cs b/src/Catalyst.Core.Lib/P2P/Repository/IPeerRepository.cs deleted file mode 100644 index 5a3d0f008c..0000000000 --- a/src/Catalyst.Core.Lib/P2P/Repository/IPeerRepository.cs +++ /dev/null @@ -1,33 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using Catalyst.Abstractions.Repository; -using Catalyst.Core.Lib.DAO; -using Catalyst.Core.Lib.P2P.Models; - -namespace Catalyst.Core.Lib.P2P.Repository -{ - public interface IPeerRepository : IRepositoryWrapper { } - - public interface IPeerRepositoryDao : IRepositoryWrapper { } -} diff --git a/src/Catalyst.Core.Lib/P2P/Repository/PeerRepository.cs b/src/Catalyst.Core.Lib/P2P/Repository/PeerRepository.cs index 476c79e7fb..5ac61f21ae 100644 --- a/src/Catalyst.Core.Lib/P2P/Repository/PeerRepository.cs +++ b/src/Catalyst.Core.Lib/P2P/Repository/PeerRepository.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,14 +21,101 @@ #endregion +using System.Collections.Generic; +using System.Linq; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.Repository; +using Catalyst.Protocol.Peer; +using MultiFormats; +using Nethermind.Core; using SharpRepository.Repository; +using SharpRepository.Repository.Queries; +using SharpRepository.Repository.Specifications; namespace Catalyst.Core.Lib.P2P.Repository { - public class PeerRepository : RepositoryWrapper, IPeerRepository + public class PeerRepository : IPeerRepository { - public PeerRepository(IRepository repository) : base(repository) { } + private readonly IRepository _repository; + public PeerRepository(IRepository repository) { _repository = repository; } + + public Peer Get(string id) { return _repository.Get(id); } + + public Peer Get(PeerId id) + { + return _repository.Find(x => x.Address.Equals(id)); + } + + public IEnumerable GetAll() { return _repository.GetAll(); } + + public IEnumerable GetActivePeers(int count = -1) + { + return _repository.FindAll(new Specification(p => !p.IsAwolPeer)).Take(count); + } + + public IEnumerable GetActivePoaPeers() + { + return _repository.FindAll(new Specification(p => p.IsPoaNode)); + } + + public IEnumerable GetRandomPeers(int count) + { + return _repository.AsQueryable().Select(c => c.DocumentId).Shuffle().Take(count).Select(_repository.Get) + .ToList(); + } + + public IEnumerable GetPeersByAddress(MultiAddress address) + { + return _repository.FindAll(m => m.Address == address); + } + + public IEnumerable GetPeersByKvmAddress(Address kvmAddress) + { + return _repository.FindAll(m => m.KvmAddress == kvmAddress); + } + + public IEnumerable GetPoaPeersByPublicKey(string publicKeyBase58) + { + var peerId = publicKeyBase58.FromBase58().ToPeerId(); + var poaPeers = _repository.FindAll(m => m.IsPoaNode && m.Address.PeerId == peerId); + return poaPeers; + } + + public IEnumerable TakeHighestReputationPeers(int page, int count) + { + return _repository.GetAll(new PagingOptions(page, count, x => x.Reputation, isDescending: true)); + } + + public void Add(Peer peer) { _repository.Add(peer); } + + public void Add(IEnumerable peer) { _repository.Add(peer); } + + public bool Exists(string id) { return _repository.Exists(id); } + + public void Update(Peer peer) { _repository.Update(peer); } + + public uint DeletePeersByAddress(MultiAddress address) + { + var peerDeletedCount = 0u; + var peersToDelete = GetPeersByAddress(address); + + foreach (var peerToDelete in peersToDelete) + { + _repository.Delete(peerToDelete); + peerDeletedCount += 1; + } + + return peerDeletedCount; + } + + public void Delete(Peer peer) { _repository.Delete(peer); } + + public void Delete(string id) { _repository.Delete(id); } + + public int Count() { return _repository.Count(); } + public int CountActivePeers() { return _repository.FindAll(x => !x.IsAwolPeer).Count(); } + + public void Dispose() { _repository.Dispose(); } } } diff --git a/src/Catalyst.Core.Lib/P2P/ReputationSystem/IReputationManager.cs b/src/Catalyst.Core.Lib/P2P/ReputationSystem/IReputationManager.cs index fe951d97db..ff415c44c3 100644 --- a/src/Catalyst.Core.Lib/P2P/ReputationSystem/IReputationManager.cs +++ b/src/Catalyst.Core.Lib/P2P/ReputationSystem/IReputationManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,8 +22,8 @@ #endregion using System; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Abstractions.P2P.ReputationSystem; -using Catalyst.Core.Lib.P2P.Repository; namespace Catalyst.Core.Lib.P2P.ReputationSystem { diff --git a/src/Catalyst.Core.Lib/P2P/ReputationSystem/ReputationChange.cs b/src/Catalyst.Core.Lib/P2P/ReputationSystem/ReputationChange.cs index 09cbb5e7eb..268536c5f6 100644 --- a/src/Catalyst.Core.Lib/P2P/ReputationSystem/ReputationChange.cs +++ b/src/Catalyst.Core.Lib/P2P/ReputationSystem/ReputationChange.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,20 +23,24 @@ using Catalyst.Abstractions.Config; using Catalyst.Abstractions.P2P.ReputationSystem; -using Catalyst.Protocol.Peer; +using Catalyst.Core.Lib.Extensions; +using MultiFormats; +using Nethermind.Core; namespace Catalyst.Core.Lib.P2P.ReputationSystem { - internal sealed class ReputationChange + public sealed class ReputationChange : IPeerReputationChange { - public PeerId PeerId { get; } + public Address Address { get; } public IReputationEvents ReputationEvent { get; } - public ReputationChange(PeerId peerIdentifier, IReputationEvents reputationEvent) + public ReputationChange(Address address, IReputationEvents reputationEvent) { - PeerId = peerIdentifier; + Address = address; ReputationEvent = reputationEvent; } + + public ReputationChange(MultiAddress address, IReputationEvents reputationEvent) : this(address.GetKvmAddress(), reputationEvent) { } } } diff --git a/src/Catalyst.Core.Lib/P2P/ReputationSystem/ReputationManager.cs b/src/Catalyst.Core.Lib/P2P/ReputationSystem/ReputationManager.cs index e78899b893..2412befa53 100644 --- a/src/Catalyst.Core.Lib/P2P/ReputationSystem/ReputationManager.cs +++ b/src/Catalyst.Core.Lib/P2P/ReputationSystem/ReputationManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,13 +22,11 @@ #endregion using System; -using System.Linq; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Reactive.Subjects; -using System.Threading; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Abstractions.P2P.ReputationSystem; -using Catalyst.Core.Lib.P2P.Repository; using Dawn; using Serilog; @@ -41,19 +39,18 @@ public sealed class ReputationManager : IReputationManager, IDisposable public readonly ReplaySubject ReputationEvent; public IObservable ReputationEventStream => ReputationEvent.AsObservable(); public IObservable MergedEventStream { get; set; } - static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1); - public ReputationManager(IPeerRepository peerRepository, ILogger logger) + public ReputationManager(IPeerRepository peerRepository, ILogger logger, IScheduler scheduler = null) { + var observableScheduler = scheduler ?? Scheduler.Default; + _logger = logger; PeerRepository = peerRepository; - ReputationEvent = new ReplaySubject(0); - - ReputationEventStream - .SubscribeOn(NewThreadScheduler.Default) - .Subscribe(OnNext, OnError, OnCompleted); + ReputationEvent = new ReplaySubject(0, observableScheduler); + + ReputationEventStream.Subscribe(OnNext, OnError, OnCompleted); } - + /// /// Allows passing a reputation streams to merge with the MasterReputationEventStream /// @@ -64,44 +61,33 @@ public void MergeReputationStream(IObservable reputationC MergedEventStream = ReputationEventStream.Merge(reputationChangeStream); } - private void OnCompleted() - { - _logger.Debug("Message stream ended."); - } + private void OnCompleted() { _logger.Debug("Message stream ended."); } - private void OnError(Exception obj) - { - _logger.Error("Message stream ended."); - } + private void OnError(Exception exc) { _logger.Error("ReputationManager error: {@Exc}", exc); } - // ReSharper disable once VSTHRD100 - public async void OnNext(IPeerReputationChange peerReputationChange) + public void OnNext(IPeerReputationChange peerReputationChange) { try { - await SemaphoreSlim.WaitAsync().ConfigureAwait(false); - - var peer = PeerRepository.GetAll().FirstOrDefault(p => p.PeerId.Equals(peerReputationChange.PeerId)); - Guard.Argument(peer, nameof(peer)).NotNull(); + var peers = PeerRepository.GetPeersByKvmAddress(peerReputationChange.Address); + foreach (var peer in peers) + { + Guard.Argument(peer, nameof(peer)).NotNull(); - // ReSharper disable once PossibleNullReferenceException - peer.Reputation += peerReputationChange.ReputationEvent.Amount; - PeerRepository.Update(peer); + peer.Reputation += peerReputationChange.ReputationEvent.Amount; + PeerRepository.Update(peer); + } } catch (Exception e) { _logger.Error(e, e.Message); } - finally - { - SemaphoreSlim.Release(); - } } - + public void Dispose() { ReputationEvent?.Dispose(); - PeerRepository?.Dispose(); + PeerRepository?.Dispose(); } } } diff --git a/src/Catalyst.Core.Lib/Repository/RepositoryWrapper.cs b/src/Catalyst.Core.Lib/Repository/RepositoryWrapper.cs deleted file mode 100644 index 476ab99870..0000000000 --- a/src/Catalyst.Core.Lib/Repository/RepositoryWrapper.cs +++ /dev/null @@ -1,840 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Linq.Expressions; -using Catalyst.Abstractions.Repository; -using SharpRepository.Repository; -using SharpRepository.Repository.Caching; -using SharpRepository.Repository.FetchStrategies; -using SharpRepository.Repository.Queries; -using SharpRepository.Repository.Specifications; -using SharpRepository.Repository.Transactions; - -namespace Catalyst.Core.Lib.Repository -{ - public class RepositoryWrapper : IRepositoryWrapper where T : class - { - public RepositoryWrapper(IRepository repository) - { - Repository = repository; - } - - protected readonly IRepository Repository; - - public IRepositoryConventions Conventions - { - get => Repository.Conventions; - set => Repository.Conventions = value; - } - - public Type EntityType => Repository.EntityType; - - public Type KeyType => Repository.KeyType; - - public ICachingStrategy CachingStrategy - { - get => Repository.CachingStrategy; - set => Repository.CachingStrategy = value; - } - - public bool CachingEnabled { get => Repository.CachingEnabled; set => Repository.CachingEnabled = value; } - - public bool CacheUsed => Repository.CacheUsed; - - public string TraceInfo => Repository.TraceInfo; - - public bool GenerateKeyOnAdd { get => Repository.GenerateKeyOnAdd; set => Repository.GenerateKeyOnAdd = value; } - - public void Add(T entity) - { - Repository.Add(entity); - } - - public void Add(IEnumerable entities) - { - Repository.Add(entities); - } - - public IQueryable AsQueryable() - { - return Repository.AsQueryable(); - } - - public double Average(Expression> selector) - { - return Repository.Average(selector); - } - - public double Average(ISpecification criteria, Expression> selector) - { - return Repository.Average(criteria, selector); - } - - public double Average(Expression> predicate, Expression> selector) - { - return Repository.Average(predicate, selector); - } - - public double? Average(Expression> selector) - { - return Repository.Average(selector); - } - - public double? Average(ISpecification criteria, Expression> selector) - { - return Repository.Average(criteria, selector); - } - - public double? Average(Expression> predicate, Expression> selector) - { - return Repository.Average(predicate, selector); - } - - public double Average(Expression> selector) - { - return Repository.Average(selector); - } - - public double Average(ISpecification criteria, Expression> selector) - { - return Repository.Average(criteria, selector); - } - - public double Average(Expression> predicate, Expression> selector) - { - return Repository.Average(predicate, selector); - } - - public double? Average(Expression> selector) - { - return Repository.Average(selector); - } - - public double? Average(ISpecification criteria, Expression> selector) - { - return Repository.Average(criteria, selector); - } - - public double? Average(Expression> predicate, Expression> selector) - { - return Repository.Average(predicate, selector); - } - - public decimal Average(Expression> selector) - { - return Repository.Average(selector); - } - - public decimal Average(ISpecification criteria, Expression> selector) - { - return Repository.Average(criteria, selector); - } - - public decimal Average(Expression> predicate, Expression> selector) - { - return Repository.Average(predicate, selector); - } - - public decimal? Average(Expression> selector) - { - return Repository.Average(selector); - } - - public decimal? Average(ISpecification criteria, Expression> selector) - { - return Repository.Average(criteria, selector); - } - - public decimal? Average(Expression> predicate, Expression> selector) - { - return Repository.Average(predicate, selector); - } - - public double Average(Expression> selector) - { - return Repository.Average(selector); - } - - public double Average(ISpecification criteria, Expression> selector) - { - return Repository.Average(criteria, selector); - } - - public double Average(Expression> predicate, Expression> selector) - { - return Repository.Average(predicate, selector); - } - - public double? Average(Expression> selector) - { - return Repository.Average(selector); - } - - public double? Average(ISpecification criteria, Expression> selector) - { - return Repository.Average(criteria, selector); - } - - public double? Average(Expression> predicate, Expression> selector) - { - return Repository.Average(predicate, selector); - } - - public float Average(Expression> selector) - { - return Repository.Average(selector); - } - - public float Average(ISpecification criteria, Expression> selector) - { - return Repository.Average(criteria, selector); - } - - public float Average(Expression> predicate, Expression> selector) - { - return Repository.Average(predicate, selector); - } - - public float? Average(Expression> selector) - { - return Repository.Average(selector); - } - - public float? Average(ISpecification criteria, Expression> selector) - { - return Repository.Average(criteria, selector); - } - - public float? Average(Expression> predicate, Expression> selector) - { - return Repository.Average(predicate, selector); - } - - public IBatch BeginBatch() - { - return Repository.BeginBatch(); - } - - public void ClearCache() - { - Repository.ClearCache(); - } - - public int Count() - { - return Repository.Count(); - } - - public int Count(ISpecification criteria) - { - return Repository.Count(criteria); - } - - public int Count(Expression> predicate) - { - return Repository.Count(predicate); - } - - public void Delete(string key) - { - Repository.Delete(key); - } - - public void Delete(IEnumerable keys) - { - Repository.Delete(keys); - } - - public void Delete(params string[] keys) - { - Repository.Delete(keys); - } - - public void Delete(T entity) - { - Repository.Delete(entity); - } - - public void Delete(IEnumerable entities) - { - Repository.Delete(entities); - } - - public void Delete(Expression> predicate) - { - Repository.Delete(predicate); - } - - public void Delete(ISpecification criteria) - { - Repository.Delete(criteria); - } - - public IDisabledCache DisableCaching() - { - return Repository.DisableCaching(); - } - - public void Dispose() - { - Repository.Dispose(); - } - - public bool Exists(string key) - { - return Repository.Exists(key); - } - - public bool Exists(Expression> predicate) - { - return Repository.Exists(predicate); - } - - public bool Exists(ISpecification criteria) - { - return Repository.Exists(criteria); - } - - public T Find(Expression> predicate, IQueryOptions queryOptions = null) - { - return Repository.Find(predicate, queryOptions); - } - - public TResult Find(Expression> predicate, Expression> selector, IQueryOptions queryOptions = null) - { - return Repository.Find(predicate, selector, queryOptions); - } - - public T Find(ISpecification criteria, IQueryOptions queryOptions = null) - { - return Repository.Find(criteria, queryOptions); - } - - public TResult Find(ISpecification criteria, Expression> selector, IQueryOptions queryOptions = null) - { - return Repository.Find(criteria, selector, queryOptions); - } - - public IEnumerable FindAll(Expression> predicate, IQueryOptions queryOptions = null) - { - return Repository.FindAll(predicate, queryOptions); - } - - public IEnumerable FindAll(Expression> predicate, Expression> selector, IQueryOptions queryOptions = null) - { - return Repository.FindAll(predicate, selector, queryOptions); - } - - public IEnumerable FindAll(ISpecification criteria, IQueryOptions queryOptions = null) - { - return Repository.FindAll(criteria, queryOptions); - } - - public IEnumerable FindAll(ISpecification criteria, Expression> selector, IQueryOptions queryOptions = null) - { - return Repository.FindAll(criteria, selector, queryOptions); - } - - public T Get(string key) - { - return Repository.Get(key); - } - - public T Get(string key, IFetchStrategy fetchStrategy) - { - return Repository.Get(key, fetchStrategy); - } - - public T Get(string key, params string[] includePaths) - { - return Repository.Get(key, includePaths); - } - - public T Get(string key, params Expression>[] includePaths) - { - return Repository.Get(key, includePaths); - } - - public TResult Get(string key, Expression> selector) - { - return Repository.Get(key, selector); - } - - public TResult Get(string key, Expression> selector, IFetchStrategy fetchStrategy) - { - return Repository.Get(key, selector, fetchStrategy); - } - - public TResult Get(string key, Expression> selector, params Expression>[] includePaths) - { - return Repository.Get(key, selector, includePaths); - } - - public TResult Get(string key, Expression> selector, params string[] includePaths) - { - return Repository.Get(key, selector, includePaths); - } - - public IEnumerable GetAll() - { - return Repository.GetAll(); - } - - public IEnumerable GetAll(IFetchStrategy fetchStrategy) - { - return Repository.GetAll(fetchStrategy); - } - - public IEnumerable GetAll(params string[] includePaths) - { - return Repository.GetAll(includePaths); - } - - public IEnumerable GetAll(params Expression>[] includePaths) - { - return Repository.GetAll(includePaths); - } - - public IEnumerable GetAll(IQueryOptions queryOptions) - { - return Repository.GetAll(queryOptions); - } - - public IEnumerable GetAll(IQueryOptions queryOptions, IFetchStrategy fetchStrategy) - { - return Repository.GetAll(queryOptions, fetchStrategy); - } - - public IEnumerable GetAll(IQueryOptions queryOptions, params string[] includePaths) - { - return Repository.GetAll(queryOptions, includePaths); - } - - public IEnumerable GetAll(IQueryOptions queryOptions, params Expression>[] includePaths) - { - return Repository.GetAll(queryOptions, includePaths); - } - - public IEnumerable GetAll(Expression> selector) - { - return Repository.GetAll(selector); - } - - public IEnumerable GetAll(Expression> selector, IQueryOptions queryOptions) - { - return Repository.GetAll(selector, queryOptions); - } - - public IEnumerable GetAll(Expression> selector, IFetchStrategy fetchStrategy) - { - return Repository.GetAll(selector, fetchStrategy); - } - - public IEnumerable GetAll(Expression> selector, params string[] includePaths) - { - return Repository.GetAll(selector, includePaths); - } - - public IEnumerable GetAll(Expression> selector, params Expression>[] includePaths) - { - return Repository.GetAll(selector, includePaths); - } - - public IEnumerable GetAll(Expression> selector, IQueryOptions queryOptions, IFetchStrategy fetchStrategy) - { - return Repository.GetAll(selector, queryOptions, fetchStrategy); - } - - public IEnumerable GetAll(Expression> selector, IQueryOptions queryOptions, params string[] includePaths) - { - return Repository.GetAll(selector, queryOptions, includePaths); - } - - public IEnumerable GetAll(Expression> selector, IQueryOptions queryOptions, params Expression>[] includePaths) - { - return Repository.GetAll(selector, queryOptions, includePaths); - } - - public IEnumerable GetMany(params string[] keys) - { - return Repository.GetMany(keys); - } - - public IEnumerable GetMany(IEnumerable keys) - { - return Repository.GetMany(keys); - } - - public IEnumerable GetMany(IEnumerable keys, IFetchStrategy fetchStrategy) - { - return Repository.GetMany(keys, fetchStrategy); - } - - public IEnumerable GetMany(Expression> selector, params string[] keys) - { - return Repository.GetMany(selector, keys); - } - - public IEnumerable GetMany(IEnumerable keys, Expression> selector) - { - return Repository.GetMany(keys, selector); - } - - public IDictionary GetManyAsDictionary(params string[] keys) - { - return Repository.GetManyAsDictionary(keys); - } - - public IDictionary GetManyAsDictionary(IEnumerable keys) - { - return Repository.GetManyAsDictionary(keys); - } - - public IDictionary GetManyAsDictionary(IEnumerable keys, IFetchStrategy fetchStrategy) - { - return Repository.GetManyAsDictionary(keys, fetchStrategy); - } - - public string GetPrimaryKey(T entity) - { - return Repository.GetPrimaryKey(entity); - } - - public IEnumerable GroupBy(Expression> keySelector, Expression, TResult>> resultSelector) - { - return Repository.GroupBy(keySelector, resultSelector); - } - - public IEnumerable GroupBy(ISpecification criteria, - Expression> keySelector, - Expression, TResult>> resultSelector) - { - return Repository.GroupBy(criteria, keySelector, resultSelector); - } - - public IEnumerable GroupBy(Expression> predicate, - Expression> keySelector, - Expression, TResult>> resultSelector) - { - return Repository.GroupBy(predicate, keySelector, resultSelector); - } - - public IDictionary GroupCount(Expression> selector) - { - return Repository.GroupCount(selector); - } - - public IDictionary GroupCount(ISpecification criteria, Expression> selector) - { - return Repository.GroupCount(criteria, selector); - } - - public IDictionary GroupCount(Expression> predicate, Expression> selector) - { - return Repository.GroupCount(predicate, selector); - } - - public IDictionary GroupLongCount(Expression> selector) - { - return Repository.GroupLongCount(selector); - } - - public IDictionary GroupLongCount(ISpecification criteria, Expression> selector) - { - return Repository.GroupLongCount(criteria, selector); - } - - public IDictionary GroupLongCount(Expression> predicate, - Expression> - selector) - { - return Repository.GroupLongCount(predicate, selector); - } - - public IRepositoryQueryable Join(IRepositoryQueryable innerRepository, - Expression> outerKeySelector, - Expression> innerKeySelector, - Expression> resultSelector) where TInner : class where TResult : class - { - return Repository.Join(innerRepository, outerKeySelector, innerKeySelector, resultSelector); - } - - public long LongCount() - { - return Repository.LongCount(); - } - - public long LongCount(ISpecification criteria) - { - return Repository.LongCount(criteria); - } - - public long LongCount(Expression> predicate) - { - return Repository.LongCount(predicate); - } - - public TResult Max(Expression> selector) - { - return Repository.Max(selector); - } - - public TResult Max(ISpecification criteria, Expression> selector) - { - return Repository.Max(criteria, selector); - } - - public TResult Max(Expression> predicate, Expression> selector) - { - return Repository.Max(predicate, selector); - } - - public TResult Min(Expression> selector) - { - return Repository.Min(selector); - } - - public TResult Min(ISpecification criteria, Expression> selector) - { - return Repository.Min(criteria, selector); - } - - public TResult Min(Expression> predicate, Expression> selector) - { - return Repository.Min(predicate, selector); - } - - public int Sum(Expression> selector) - { - return Repository.Sum(selector); - } - - public int Sum(ISpecification criteria, Expression> selector) - { - return Repository.Sum(criteria, selector); - } - - public int Sum(Expression> predicate, Expression> selector) - { - return Repository.Sum(predicate, selector); - } - - public int? Sum(Expression> selector) - { - return Repository.Sum(selector); - } - - public int? Sum(ISpecification criteria, Expression> selector) - { - return Repository.Sum(criteria, selector); - } - - public int? Sum(Expression> predicate, Expression> selector) - { - return Repository.Sum(predicate, selector); - } - - public long Sum(Expression> selector) - { - return Repository.Sum(selector); - } - - public long Sum(ISpecification criteria, Expression> selector) - { - return Repository.Sum(criteria, selector); - } - - public long Sum(Expression> predicate, Expression> selector) - { - return Repository.Sum(predicate, selector); - } - - public long? Sum(Expression> selector) - { - return Repository.Sum(selector); - } - - public long? Sum(ISpecification criteria, Expression> selector) - { - return Repository.Sum(criteria, selector); - } - - public long? Sum(Expression> predicate, Expression> selector) - { - return Repository.Sum(predicate, selector); - } - - public decimal Sum(Expression> selector) - { - return Repository.Sum(selector); - } - - public decimal Sum(ISpecification criteria, Expression> selector) - { - return Repository.Sum(criteria, selector); - } - - public decimal Sum(Expression> predicate, Expression> selector) - { - return Repository.Sum(predicate, selector); - } - - public decimal? Sum(Expression> selector) - { - return Repository.Sum(selector); - } - - public decimal? Sum(ISpecification criteria, Expression> selector) - { - return Repository.Sum(criteria, selector); - } - - public decimal? Sum(Expression> predicate, Expression> selector) - { - return Repository.Sum(predicate, selector); - } - - public double Sum(Expression> selector) - { - return Repository.Sum(selector); - } - - public double Sum(ISpecification criteria, Expression> selector) - { - return Repository.Sum(criteria, selector); - } - - public double Sum(Expression> predicate, Expression> selector) - { - return Repository.Sum(predicate, selector); - } - - public double? Sum(Expression> selector) - { - return Repository.Sum(selector); - } - - public double? Sum(ISpecification criteria, Expression> selector) - { - return Repository.Sum(criteria, selector); - } - - public double? Sum(Expression> predicate, Expression> selector) - { - return Repository.Sum(predicate, selector); - } - - public float Sum(Expression> selector) - { - return Repository.Sum(selector); - } - - public float Sum(ISpecification criteria, Expression> selector) - { - return Repository.Sum(criteria, selector); - } - - public float Sum(Expression> predicate, Expression> selector) - { - return Repository.Sum(predicate, selector); - } - - public float? Sum(Expression> selector) - { - return Repository.Sum(selector); - } - - public float? Sum(ISpecification criteria, Expression> selector) - { - return Repository.Sum(criteria, selector); - } - - public float? Sum(Expression> predicate, Expression> selector) - { - return Repository.Sum(predicate, selector); - } - - public bool TryFind(Expression> predicate, out T entity) - { - return Repository.TryFind(predicate, out entity); - } - - public bool TryFind(Expression> predicate, IQueryOptions queryOptions, out T entity) - { - return Repository.TryFind(predicate, queryOptions, out entity); - } - - public bool TryFind(Expression> predicate, Expression> selector, out TResult entity) - { - return Repository.TryFind(predicate, selector, out entity); - } - - public bool TryFind(Expression> predicate, Expression> selector, IQueryOptions queryOptions, out TResult entity) - { - return Repository.TryFind(predicate, selector, queryOptions, out entity); - } - - public bool TryFind(ISpecification criteria, out T entity) - { - return Repository.TryFind(criteria, out entity); - } - - public bool TryFind(ISpecification criteria, IQueryOptions queryOptions, out T entity) - { - return Repository.TryFind(criteria, queryOptions, out entity); - } - - public bool TryFind(ISpecification criteria, Expression> selector, out TResult entity) - { - return Repository.TryFind(criteria, selector, out entity); - } - - public bool TryFind(ISpecification criteria, Expression> selector, IQueryOptions queryOptions, out TResult entity) - { - return Repository.TryFind(criteria, selector, queryOptions, out entity); - } - - public bool TryGet(string key, out T entity) - { - return Repository.TryGet(key, out entity); - } - - public bool TryGet(string key, Expression> selector, out TResult entity) - { - return Repository.TryGet(key, selector, out entity); - } - - public void Update(T entity) - { - Repository.Update(entity); - } - - public void Update(IEnumerable entities) - { - Repository.Update(entities); - } - } -} diff --git a/src/Catalyst.Core.Lib/Rpc/IO/Exceptions/ResponseHandlerDoesNotExistException.cs b/src/Catalyst.Core.Lib/Rpc/IO/Exceptions/ResponseHandlerDoesNotExistException.cs index ffa74bdab2..9ebb0804b1 100644 --- a/src/Catalyst.Core.Lib/Rpc/IO/Exceptions/ResponseHandlerDoesNotExistException.cs +++ b/src/Catalyst.Core.Lib/Rpc/IO/Exceptions/ResponseHandlerDoesNotExistException.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManager.cs b/src/Catalyst.Core.Lib/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManager.cs index 680a994fcc..e1c937c311 100644 --- a/src/Catalyst.Core.Lib/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManager.cs +++ b/src/Catalyst.Core.Lib/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -58,7 +58,7 @@ protected override void EvictionCallback(object key, object value, EvictionReaso { Logger.Verbose("{key} message evicted", (key as ByteString).ToCorrelationId()); var message = (CorrelatableMessage) value; - _evictionEvent.OnNext(new MessageEvictionEvent(message, message.Content.PeerId)); + _evictionEvent.OnNext(new MessageEvictionEvent(message, message.Content.Address)); } protected override void Dispose(bool disposing) diff --git a/src/Catalyst.Core.Lib/Rpc/IO/RpcResponseObserver.cs b/src/Catalyst.Core.Lib/Rpc/IO/RpcResponseObserver.cs deleted file mode 100644 index 8c17c94c44..0000000000 --- a/src/Catalyst.Core.Lib/Rpc/IO/RpcResponseObserver.cs +++ /dev/null @@ -1,55 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Protocol.Peer; -using Dawn; -using DotNetty.Transport.Channels; -using Google.Protobuf; -using Serilog; - -namespace Catalyst.Core.Lib.Rpc.IO -{ - public abstract class RpcResponseObserver : ResponseObserverBase, IRpcResponseObserver - where TProto : IMessage - { - protected RpcResponseObserver(ILogger logger, bool assertMessageNameCheck = true) : base(logger, - assertMessageNameCheck) { } - - protected abstract override void HandleResponse(TProto messageDto, IChannelHandlerContext channelHandlerContext, PeerId senderPeerId, ICorrelationId correlationId); - - public void HandleResponseObserver(IMessage message, - IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, - ICorrelationId correlationId) - { - Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); - Guard.Argument(message, nameof(message)).NotNull("The message cannot be null"); - - HandleResponse((TProto) message, channelHandlerContext, senderPeerId, correlationId); - } - } -} diff --git a/src/Catalyst.Core.Lib/Service/DeltaIndexService.cs b/src/Catalyst.Core.Lib/Service/DeltaIndexService.cs new file mode 100644 index 0000000000..03ddff6d91 --- /dev/null +++ b/src/Catalyst.Core.Lib/Service/DeltaIndexService.cs @@ -0,0 +1,110 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using Catalyst.Core.Lib.DAO.Ledger; +using Lib.P2P; +using SharpRepository.Repository; +using SharpRepository.Repository.Queries; + +namespace Catalyst.Core.Lib.Service +{ + public class DeltaIndexService : IDeltaIndexService + { + private bool _disposed; + private readonly IRepository _repository; + + public DeltaIndexService(IRepository repository) { _repository = repository; } + + public void Add(IEnumerable deltaIndexes) + { + _repository.Add(deltaIndexes); + } + + public void Add(DeltaIndexDao deltaIndex) + { + _repository.Add(deltaIndex); + } + + public IEnumerable GetRange(ulong start, ulong count) + { + return _repository.FindAll(x => x.Height >= start && x.Height <= start + count).OrderBy(x => x.Height); + } + + public ulong Height() + { + var deltaIndex = LatestDeltaIndex(); + if (deltaIndex == null) + { + return 0; + } + return deltaIndex.Height; + } + + public DeltaIndexDao LatestDeltaIndex() + { + PagingOptions pagingOptions = new(1, 2, x => x.Height, isDescending: true); + return _repository.GetAll(pagingOptions).FirstOrDefault(); + } + + public void Map(long deltaNumber, Cid deltaHash) + { + if (!_repository.TryGet(DeltaIndexDao.BuildDocumentId((ulong)deltaNumber), out _)) + { + _repository.Add(new DeltaIndexDao { Height = (ulong)deltaNumber, Cid = deltaHash }); + } + } + + public bool TryFind(long deltaNumber, out Cid deltaHash) + { + if (_repository.TryGet(DeltaIndexDao.BuildDocumentId((ulong)deltaNumber), out var delta)) + { + deltaHash = delta.Cid; + return true; + } + + deltaHash = default; + return false; + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _repository.Dispose(); + } + } + _disposed = true; + } + } +} diff --git a/src/Catalyst.Core.Lib/Repository/EfCoreContext.cs b/src/Catalyst.Core.Lib/Service/EfCoreContext.cs similarity index 89% rename from src/Catalyst.Core.Lib/Repository/EfCoreContext.cs rename to src/Catalyst.Core.Lib/Service/EfCoreContext.cs index 4695b52ee3..8b71f85887 100644 --- a/src/Catalyst.Core.Lib/Repository/EfCoreContext.cs +++ b/src/Catalyst.Core.Lib/Service/EfCoreContext.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,9 +23,12 @@ using System; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Cryptography; +using Catalyst.Core.Lib.DAO.Peer; +using Catalyst.Core.Lib.DAO.Transaction; using Microsoft.EntityFrameworkCore; -namespace Catalyst.Core.Lib.Repository +namespace Catalyst.Core.Lib.Service { public interface IDbContext : IDisposable { @@ -43,8 +46,6 @@ public EfCoreContext(string connectionString) public DbSet TransactionBroadcastStore { get; set; } public DbSet PublicEntryDaoStore { get; set; } public DbSet ConfidentialEntryDaoStore { get; set; } - public DbSet ContractEntryDaoStore { get; set; } - public DbSet BaseEntryDaoStore { get; set; } public DbSet SignatureDaoStore { get; set; } public DbSet SigningContextDaoStore { get; set; } diff --git a/src/Catalyst.Core.Lib/Repository/FileSystemAwareXmlRepository.cs b/src/Catalyst.Core.Lib/Service/FileSystemAwareXmlRepository.cs similarity index 88% rename from src/Catalyst.Core.Lib/Repository/FileSystemAwareXmlRepository.cs rename to src/Catalyst.Core.Lib/Service/FileSystemAwareXmlRepository.cs index 2b6adff8cc..e06b4f7135 100644 --- a/src/Catalyst.Core.Lib/Repository/FileSystemAwareXmlRepository.cs +++ b/src/Catalyst.Core.Lib/Service/FileSystemAwareXmlRepository.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,14 +25,14 @@ using Catalyst.Abstractions.FileSystem; using SharpRepository.XmlRepository; -namespace Catalyst.Core.Lib.Repository +namespace Catalyst.Core.Lib.Service { /// /// Xml Repository where base folder is derived from the file system /// /// Type of object /// - public class FileSystemAwareXmlRepository : XmlRepository where T : class, new() + public sealed class FileSystemAwareXmlRepository : XmlRepository where T : class, new() { public FileSystemAwareXmlRepository(IFileSystem fileSystem, string path = "") : base(Path.Combine(fileSystem.GetCatalystDataDir().ToString(), path)) { } } diff --git a/src/Catalyst.Core.Lib/Service/IDeltaIndexService.cs b/src/Catalyst.Core.Lib/Service/IDeltaIndexService.cs new file mode 100644 index 0000000000..181169891a --- /dev/null +++ b/src/Catalyst.Core.Lib/Service/IDeltaIndexService.cs @@ -0,0 +1,41 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using Catalyst.Core.Lib.DAO.Ledger; +using Lib.P2P; + +namespace Catalyst.Core.Lib.Service +{ + public interface IDeltaIndexService : IDisposable + { + void Add(DeltaIndexDao deltaIndex); + void Add(IEnumerable deltaIndexes); + ulong Height(); + IEnumerable GetRange(ulong start, ulong count); + DeltaIndexDao LatestDeltaIndex(); + void Map(long deltaNumber, Cid deltaHash); + bool TryFind(long deltaNumber, out Cid deltaHash); + } +} diff --git a/src/Catalyst.Core.Lib/Util/ByteUtil.cs b/src/Catalyst.Core.Lib/Util/ByteUtil.cs index aa4f534d02..49a51f8143 100644 --- a/src/Catalyst.Core.Lib/Util/ByteUtil.cs +++ b/src/Catalyst.Core.Lib/Util/ByteUtil.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,7 +30,7 @@ namespace Catalyst.Core.Lib.Util { public static class ByteUtil { - private static readonly Random Rand = new Random(); + private static readonly Random Rand = new(); public static byte[] ZeroByteArray { get; } = {0}; public static byte[] EmptyByteArray { get; } = new byte[0]; diff --git a/src/Catalyst.Core.Lib/Util/CancellationTokenProvider.cs b/src/Catalyst.Core.Lib/Util/CancellationTokenProvider.cs index 026d12de5d..d085ad0aee 100644 --- a/src/Catalyst.Core.Lib/Util/CancellationTokenProvider.cs +++ b/src/Catalyst.Core.Lib/Util/CancellationTokenProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,12 +29,27 @@ namespace Catalyst.Core.Lib.Util { public sealed class CancellationTokenProvider : ICancellationTokenProvider, IDisposable { - public CancellationTokenSource CancellationTokenSource { get; set; } + public CancellationTokenSource CancellationTokenSource { get; } - public CancellationTokenProvider() + public CancellationTokenProvider(bool goodTillCancel) { + if (!goodTillCancel) + { + CancellationTokenSource = new CancellationTokenSource(10); + } + CancellationTokenSource = new CancellationTokenSource(); } + + public CancellationTokenProvider(TimeSpan timeToLiveInMs) + { + CancellationTokenSource = new CancellationTokenSource(timeToLiveInMs); + } + + public CancellationTokenProvider(int seconds) + { + CancellationTokenSource = new CancellationTokenSource(TimeSpan.FromSeconds(seconds)); + } public bool HasTokenCancelled() { diff --git a/src/Catalyst.Core.Lib/Util/CommandFormatHelper.cs b/src/Catalyst.Core.Lib/Util/CommandFormatHelper.cs index 81042324a7..bb1a150fcd 100644 --- a/src/Catalyst.Core.Lib/Util/CommandFormatHelper.cs +++ b/src/Catalyst.Core.Lib/Util/CommandFormatHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -39,7 +39,7 @@ public static class CommandFormatHelper /// String of the formatted response public static string FormatRepeatedPeerInfoResponse(RepeatedField repeatedPeerInfo) { - var stringBuilder = new StringBuilder(); + StringBuilder stringBuilder = new(); foreach (var peerInfo in repeatedPeerInfo) { stringBuilder.AppendLine($"BlackListed={peerInfo.IsBlacklisted}"); diff --git a/src/Catalyst.Core.Lib/Util/Duration.cs b/src/Catalyst.Core.Lib/Util/Duration.cs new file mode 100644 index 0000000000..a1a4681609 --- /dev/null +++ b/src/Catalyst.Core.Lib/Util/Duration.cs @@ -0,0 +1,201 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Globalization; +using System.IO; +using System.Text; + +namespace Catalyst.Core.Lib.Util +{ + /// + /// Parsing and stringifying of a according to IPFS. + /// + /// + /// From the go spec. + /// + /// A duration string is a possibly signed sequence of decimal numbers, + /// each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". + /// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + /// + /// + public static class Duration + { + private const double TicksPerNanosecond = TimeSpan.TicksPerMillisecond * 0.000001; + private const double TicksPerMicrosecond = TimeSpan.TicksPerMillisecond * 0.001; + + /// + /// Converts the string representation of an IPFS duration + /// into its equivalent. + /// + /// + /// A string that contains the duration to convert. + /// + /// + /// A that is equivalent to . + /// + /// + /// is not a valid IPFS duration. + /// + /// + /// An empty string or "n/a" or "unknown" returns . + /// + /// A duration string is a possibly signed sequence of decimal numbers, + /// each with optional fraction and a unit suffix, such as "300ms", "-1.5h" or "2h45m". + /// Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h". + /// + /// + public static TimeSpan Parse(string s) + { + if (string.IsNullOrWhiteSpace(s) || s == "n/a" || s == "unknown") + return TimeSpan.Zero; + + var result = TimeSpan.Zero; + var negative = false; + using (StringReader sr = new(s)) + { + if (sr.Peek() == '-') + { + negative = true; + sr.Read(); + } + + while (sr.Peek() != -1) + { + result += ParseComponent(sr); + } + } + + if (negative) + return TimeSpan.FromTicks(-result.Ticks); + + return result; + } + + private static TimeSpan ParseComponent(StringReader reader) + { + var value = ParseNumber(reader); + var unit = ParseUnit(reader); + + switch (unit) + { + case "h": + return TimeSpan.FromHours(value); + case "m": + return TimeSpan.FromMinutes(value); + case "s": + return TimeSpan.FromSeconds(value); + case "ms": + return TimeSpan.FromMilliseconds(value); + case "us": + case "µs": + return TimeSpan.FromTicks((long) (value * TicksPerMicrosecond)); + case "ns": + return TimeSpan.FromTicks((long) (value * TicksPerNanosecond)); + case "": + throw new FormatException("Missing IPFS duration unit."); + default: + throw new FormatException($"Unknown IPFS duration unit '{unit}'."); + } + } + + private static double ParseNumber(StringReader reader) + { + StringBuilder s = new(); + while (true) + { + var c = (char) reader.Peek(); + if (char.IsDigit(c) || c == '.') + { + s.Append(c); + reader.Read(); + continue; + } + + return double.Parse(s.ToString(), CultureInfo.InvariantCulture); + } + } + + private static string ParseUnit(StringReader reader) + { + StringBuilder s = new(); + while (true) + { + var c = (char) reader.Peek(); + if (char.IsDigit(c) || c == '.' || c == (char) 0xFFFF) + break; + s.Append(c); + reader.Read(); + } + + return s.ToString(); + } + + /// + /// Converts the to the equivalent string representation of an + /// IPFS duration. + /// + /// + /// The to convert. + /// + /// + /// The string representation, when the + /// is equal to / + /// + /// + /// The IPFS duration string representation. + /// + public static string Stringify(TimeSpan duration, string zeroValue = "0s") + { + if (duration.Ticks == 0) + return zeroValue; + + StringBuilder s = new(); + if (duration.Ticks < 0) + { + s.Append('-'); + duration = TimeSpan.FromTicks(-duration.Ticks); + } + + Stringify(Math.Floor(duration.TotalHours), "h", s); + Stringify(duration.Minutes, "m", s); + Stringify(duration.Seconds, "s", s); + Stringify(duration.Milliseconds, "ms", s); + Stringify((long) (duration.Ticks / TicksPerMicrosecond) % 1000, "us", s); + Stringify((long) (duration.Ticks / TicksPerNanosecond) % 1000, "ns", s); + + return s.ToString(); + } + + private static void Stringify(double value, string unit, StringBuilder sb) + { + if (value == 0) + { + return; + } + + sb.Append(value.ToString(CultureInfo.InvariantCulture)); + sb.Append(unit); + } + } +} diff --git a/src/Catalyst.Core.Lib/Util/IpAddressConverter.cs b/src/Catalyst.Core.Lib/Util/IpAddressConverter.cs index 807cc01d97..1c50875fa2 100644 --- a/src/Catalyst.Core.Lib/Util/IpAddressConverter.cs +++ b/src/Catalyst.Core.Lib/Util/IpAddressConverter.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Util/IpEndpointConverter.cs b/src/Catalyst.Core.Lib/Util/IpEndpointConverter.cs index c51d9ed94d..9b54c4c0ac 100644 --- a/src/Catalyst.Core.Lib/Util/IpEndpointConverter.cs +++ b/src/Catalyst.Core.Lib/Util/IpEndpointConverter.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Util/JsonConverterProviders.cs b/src/Catalyst.Core.Lib/Util/JsonConverterProviders.cs index e1bce48d0e..2de30c5498 100644 --- a/src/Catalyst.Core.Lib/Util/JsonConverterProviders.cs +++ b/src/Catalyst.Core.Lib/Util/JsonConverterProviders.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -36,8 +36,7 @@ public static class JsonConverterProviders new IpEndPointConverter(), new IpAddressConverter(), new JsonProtoObjectConverter(), - new JsonProtoObjectConverter(), - new JsonProtoObjectConverter() + new JsonProtoObjectConverter() }.AsReadOnly(); } } diff --git a/src/Catalyst.Core.Lib/Util/JsonProtoObjectConverter.cs b/src/Catalyst.Core.Lib/Util/JsonProtoObjectConverter.cs index 44fe7beb19..269330e287 100644 --- a/src/Catalyst.Core.Lib/Util/JsonProtoObjectConverter.cs +++ b/src/Catalyst.Core.Lib/Util/JsonProtoObjectConverter.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,11 +29,11 @@ namespace Catalyst.Core.Lib.Util { - internal class JsonProtoObjectConverter : JsonConverter> where T : IMessage, new() + public class JsonProtoObjectConverter : JsonConverter> where T : IMessage, new() { public override void WriteJson(JsonWriter writer, IMessage value, JsonSerializer serializer) { - var formatter = new JsonFormatter(JsonFormatter.Settings.Default); + JsonFormatter formatter = new(JsonFormatter.Settings.Default); writer.WriteRawValue(formatter.Format(value)); } @@ -43,7 +43,7 @@ public override IMessage ReadJson(JsonReader reader, bool hasExistingValue, JsonSerializer serializer) { - var parser = new JsonParser(JsonParser.Settings.Default); + JsonParser parser = new(JsonParser.Settings.Default); // Load JObject from stream var jObject = JObject.Load(reader); diff --git a/src/Catalyst.Core.Lib/Util/KeyUtil.cs b/src/Catalyst.Core.Lib/Util/KeyUtil.cs index 7852b9be38..b4f201390e 100644 --- a/src/Catalyst.Core.Lib/Util/KeyUtil.cs +++ b/src/Catalyst.Core.Lib/Util/KeyUtil.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,8 @@ using Catalyst.Core.Lib.Extensions; using Google.Protobuf; -using TheDotNetLeague.MultiFormats.MultiBase; +using MultiFormats; +using System; namespace Catalyst.Core.Lib.Util { @@ -32,12 +33,12 @@ public static class KeyUtil { public static string KeyToString(this byte[] keyAsBytes) { - return Base32.ToBase32(keyAsBytes).ToLowerInvariant(); + return keyAsBytes.ToBase58(); } public static byte[] KeyToBytes(this string keyAsString) { - return keyAsString.FromBase32(); + return keyAsString.FromBase58(); } public static ByteString KeyToByteString(this string keyAsString) diff --git a/src/Catalyst.Core.Lib/Util/NodeUtil.cs b/src/Catalyst.Core.Lib/Util/NodeUtil.cs index 1a97e8e48b..a223d15c2c 100644 --- a/src/Catalyst.Core.Lib/Util/NodeUtil.cs +++ b/src/Catalyst.Core.Lib/Util/NodeUtil.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Util/NullObjects.cs b/src/Catalyst.Core.Lib/Util/NullObjects.cs index 8e88a4be94..cd7957ad46 100644 --- a/src/Catalyst.Core.Lib/Util/NullObjects.cs +++ b/src/Catalyst.Core.Lib/Util/NullObjects.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,6 @@ #endregion -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Protocol.Wire; namespace Catalyst.Core.Lib.Util @@ -29,6 +28,5 @@ namespace Catalyst.Core.Lib.Util public static class NullObjects { public static readonly ProtocolMessage ProtocolMessage = new ProtocolMessage(); - public static readonly ObserverDto ObserverDto = new ObserverDto(null, new ProtocolMessage()); } } diff --git a/src/Catalyst.Core.Lib/Util/TtlChangeTokenProvider.cs b/src/Catalyst.Core.Lib/Util/TtlChangeTokenProvider.cs index 1506860bd2..869bf30ae0 100644 --- a/src/Catalyst.Core.Lib/Util/TtlChangeTokenProvider.cs +++ b/src/Catalyst.Core.Lib/Util/TtlChangeTokenProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Lib/Validators/ListValidatorReader.cs b/src/Catalyst.Core.Lib/Validators/ListValidatorReader.cs new file mode 100644 index 0000000000..16b73d94c0 --- /dev/null +++ b/src/Catalyst.Core.Lib/Validators/ListValidatorReader.cs @@ -0,0 +1,42 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Validators; +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; +using System.Linq; + +namespace Catalyst.Core.Lib.Validators +{ + public class ListValidatorReader : IValidatorReader + { + public void AddValidatorSet(IList validatorSets, long startBlock, IConfigurationSection configurationSection) + { + if (configurationSection.Key.ToLower() == "list") + { + var addressList = configurationSection.AsEnumerable().Where(x => !string.IsNullOrWhiteSpace(x.Value)).Select(x => x.Value).ToList(); + validatorSets.Add(new ListValidatorSet(startBlock, addressList)); + } + } + } +} diff --git a/src/Catalyst.Core.Lib/Validators/ListValidatorSet.cs b/src/Catalyst.Core.Lib/Validators/ListValidatorSet.cs new file mode 100644 index 0000000000..a73a37334a --- /dev/null +++ b/src/Catalyst.Core.Lib/Validators/ListValidatorSet.cs @@ -0,0 +1,44 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Validators; +using Nethermind.Core; +using System.Collections.Generic; +using System.Linq; + +namespace Catalyst.Core.Lib.Validators +{ + public class ListValidatorSet : IValidatorSet + { + private readonly IEnumerable
_validators; + public long StartBlock { get; } + + public ListValidatorSet(long startBlock, IEnumerable validators) + { + StartBlock = startBlock; + _validators = validators.Select(x => new Address(x)); + } + + public IEnumerable
GetValidators() => _validators; + } +} diff --git a/src/Catalyst.Core.Lib/Validators/TransactionValidator.cs b/src/Catalyst.Core.Lib/Validators/TransactionValidator.cs index c143f742b0..8e1000caa4 100644 --- a/src/Catalyst.Core.Lib/Validators/TransactionValidator.cs +++ b/src/Catalyst.Core.Lib/Validators/TransactionValidator.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using System.Linq; +using Catalyst.Abstractions.Config; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.Validators; -using Catalyst.Protocol.Wire; +using Catalyst.Protocol.Transaction; using Google.Protobuf; using Serilog; @@ -34,39 +34,51 @@ public sealed class TransactionValidator : ITransactionValidator { private readonly ILogger _logger; private readonly ICryptoContext _cryptoContext; + private readonly ITransactionConfig _transactionConfig; - public TransactionValidator(ILogger logger, - ICryptoContext cryptoContext) + public TransactionValidator(ICryptoContext cryptoContext, + ITransactionConfig transactionConfig, + ILogger logger) { _cryptoContext = cryptoContext; + _transactionConfig = transactionConfig; _logger = logger; } - public bool ValidateTransaction(TransactionBroadcast transactionBroadcast) + public bool ValidateTransaction(PublicEntry transaction) { - // will add more checks - return ValidateTransactionSignature(transactionBroadcast); + if (!ValidateMinimumGasLimit(transaction)) + { + return false; + } + + return ValidateTransactionSignature(transaction); } - private bool ValidateTransactionSignature(TransactionBroadcast transactionBroadcast) + private bool ValidateMinimumGasLimit(PublicEntry transaction) + { + return transaction.GasLimit >= _transactionConfig.MinTransactionEntryGasLimit; + } + + private bool ValidateTransactionSignature(PublicEntry transaction) { - if (transactionBroadcast.Signature.RawBytes == ByteString.Empty) + if (transaction.Signature.RawBytes == ByteString.Empty) { _logger.Error("Transaction signature is null"); return false; } - var transactionSignature = _cryptoContext.GetSignatureFromBytes(transactionBroadcast.Signature.RawBytes.ToByteArray(), - transactionBroadcast.PublicEntries.First().Base.SenderPublicKey.ToByteArray()); + var transactionSignature = _cryptoContext.GetSignatureFromBytes(transaction.Signature.RawBytes.ToByteArray(), + transaction.SenderAddress.ToByteArray()); - var signingContext = transactionBroadcast.Signature.SigningContext.ToByteArray(); + var signingContext = transaction.Signature.SigningContext.ToByteArray(); // we need to verify the signature matches the message, but transactionBroadcast contains the signature and original data, // passing message+sig will mean your verifying an incorrect message and always return false, so just null the sig. - var transactionBroadcastClone = transactionBroadcast.Clone(); - transactionBroadcastClone.Signature = null; + var transactionClone = transaction.Clone(); + transactionClone.Signature = null; - if (_cryptoContext.Verify(transactionSignature, transactionBroadcastClone.ToByteArray(), signingContext)) + if (_cryptoContext.Verify(transactionSignature, transactionClone.ToByteArray(), signingContext)) { return true; } diff --git a/src/Catalyst.Core.Lib/Validators/ValidatorSetStore.cs b/src/Catalyst.Core.Lib/Validators/ValidatorSetStore.cs new file mode 100644 index 0000000000..c358c19b17 --- /dev/null +++ b/src/Catalyst.Core.Lib/Validators/ValidatorSetStore.cs @@ -0,0 +1,51 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Config; +using Catalyst.Abstractions.Validators; +using System.Collections.Generic; +using System.Linq; + +namespace Catalyst.Core.Lib.Validators +{ + public class ValidatorSetStore : IValidatorSetStore + { + private readonly SortedList _validatorSets; + + public ValidatorSetStore(IValidatorSetConfig validatorSetConfig) + { + var descendingComparer = Comparer.Create((x, y) => y.CompareTo(x)); + _validatorSets = new SortedList(descendingComparer); + + foreach (var validatorSet in validatorSetConfig.GetValidatorSetsAsync().ConfigureAwait(false).GetAwaiter().GetResult()) + { + Add(validatorSet); + } + + } + + public void Add(IValidatorSet validatorSet) => _validatorSets.Add(validatorSet.StartBlock, validatorSet); + + public IValidatorSet Get(long startBlock) => _validatorSets.FirstOrDefault(x => x.Key <= startBlock).Value; + } +} diff --git a/src/Catalyst.Core.Lib/project.assets.json b/src/Catalyst.Core.Lib/project.assets.json deleted file mode 100644 index e4dc6bf826..0000000000 --- a/src/Catalyst.Core.Lib/project.assets.json +++ /dev/null @@ -1,9347 +0,0 @@ -{ - "version": 3, - "targets": { - ".NETCoreApp,Version=v2.2": { - "Autofac/4.8.1": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.0", - "System.ComponentModel": "4.0.1" - }, - "compile": { - "lib/netstandard1.1/Autofac.dll": {} - }, - "runtime": { - "lib/netstandard1.1/Autofac.dll": {} - } - }, - "Autofac.Configuration/4.1.0": { - "type": "package", - "dependencies": { - "Autofac": "4.0.1", - "Microsoft.Extensions.Configuration": "1.0.2" - }, - "compile": { - "lib/netstandard2.0/Autofac.Configuration.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Autofac.Configuration.dll": {} - } - }, - "AutofacSerilogIntegration/2.0.0": { - "type": "package", - "dependencies": { - "Autofac": "4.0.0", - "NETStandard.Library": "1.6.0", - "Serilog": "2.0.0" - }, - "compile": { - "lib/netstandard1.1/AutofacSerilogIntegration.dll": {} - }, - "runtime": { - "lib/netstandard1.1/AutofacSerilogIntegration.dll": {} - } - }, - "BinaryEncoding/1.4.0": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.Buffers": "4.4.0" - }, - "compile": { - "lib/netstandard1.1/BinaryEncoding.dll": {} - }, - "runtime": { - "lib/netstandard1.1/BinaryEncoding.dll": {} - } - }, - "Common.Logging/3.4.1": { - "type": "package", - "dependencies": { - "Common.Logging.Core": "3.4.1", - "Microsoft.CSharp": "4.0.1", - "System.Collections": "4.0.11", - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.Reflection.TypeExtensions": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" - }, - "compile": { - "lib/netstandard1.3/Common.Logging.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Common.Logging.dll": {} - } - }, - "Common.Logging.Core/3.4.1": { - "type": "package", - "dependencies": { - "Microsoft.CSharp": "4.0.1" - }, - "compile": { - "lib/netstandard1.0/Common.Logging.Core.dll": {} - }, - "runtime": { - "lib/netstandard1.0/Common.Logging.Core.dll": {} - } - }, - "Dawn.Guard/1.9.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Dawn.Guard.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Dawn.Guard.dll": {} - } - }, - "DnsClient/1.2.0": { - "type": "package", - "dependencies": { - "System.Buffers": "4.4.0" - }, - "compile": { - "lib/netstandard2.0/DnsClient.dll": {} - }, - "runtime": { - "lib/netstandard2.0/DnsClient.dll": {} - } - }, - "DotNetty.Buffers/0.6.0": { - "type": "package", - "dependencies": { - "DotNetty.Common": "0.6.0", - "NETStandard.Library": "1.6.1", - "System.Diagnostics.Contracts": "4.3.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.2" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Buffers.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Buffers.dll": {} - } - }, - "DotNetty.Codecs/0.6.0": { - "type": "package", - "dependencies": { - "DotNetty.Buffers": "0.6.0", - "DotNetty.Common": "0.6.0", - "DotNetty.Transport": "0.6.0", - "NETStandard.Library": "1.6.1", - "System.Collections.Immutable": "1.5.0", - "System.Diagnostics.Contracts": "4.3.0" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Codecs.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Codecs.dll": {} - } - }, - "DotNetty.Codecs.Protobuf/0.6.0": { - "type": "package", - "dependencies": { - "DotNetty.Buffers": "0.6.0", - "DotNetty.Codecs": "0.6.0", - "DotNetty.Common": "0.6.0", - "DotNetty.Transport": "0.6.0", - "Google.Protobuf": "3.2.0", - "NETStandard.Library": "1.6.1", - "System.Diagnostics.Contracts": "4.3.0" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Codecs.Protobuf.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Codecs.Protobuf.dll": {} - } - }, - "DotNetty.Common/0.6.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Logging": "1.1.1", - "NETStandard.Library": "1.6.1", - "System.Diagnostics.Contracts": "4.3.0", - "System.Net.NetworkInformation": "4.3.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.2" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Common.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Common.dll": {} - } - }, - "DotNetty.Handlers/0.6.0": { - "type": "package", - "dependencies": { - "DotNetty.Buffers": "0.6.0", - "DotNetty.Codecs": "0.6.0", - "DotNetty.Common": "0.6.0", - "DotNetty.Transport": "0.6.0", - "NETStandard.Library": "1.6.1", - "System.Diagnostics.Contracts": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.Net.Security": "4.3.2" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Handlers.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Handlers.dll": {} - } - }, - "DotNetty.Transport/0.6.0": { - "type": "package", - "dependencies": { - "DotNetty.Buffers": "0.6.0", - "DotNetty.Common": "0.6.0", - "NETStandard.Library": "1.6.1", - "System.Diagnostics.Contracts": "4.3.0", - "System.Diagnostics.StackTrace": "4.3.0", - "System.Net.NameResolution": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Threading.Tasks.Extensions": "4.5.1" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Transport.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Transport.dll": {} - } - }, - "Extensions.Data.xxHash.core20/1.0.2.1": { - "type": "package", - "compile": { - "lib/netstandard2.0/Extensions.Data.xxHash.core20.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Extensions.Data.xxHash.core20.dll": {} - } - }, - "Google.Protobuf/3.9.1": { - "type": "package", - "dependencies": { - "System.Memory": "4.5.2" - }, - "compile": { - "lib/netstandard2.0/Google.Protobuf.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Google.Protobuf.dll": {} - } - }, - "Ipfs.Core/0.51.1": { - "type": "package", - "dependencies": { - "Common.Logging": "3.4.1", - "Common.Logging.Core": "3.4.1", - "Google.Protobuf": "3.7.0", - "Newtonsoft.Json": "12.0.1", - "Portable.BouncyCastle": "1.8.5", - "SimpleBase": "1.3.1" - }, - "compile": { - "lib/netstandard2.0/Ipfs.Core.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Ipfs.Core.dll": {} - } - }, - "Ipfs.Engine/0.9.1": { - "type": "package", - "dependencies": { - "Ipfs.Core": "0.51.1", - "Makaretu.Dns.Unicast": "0.9.0", - "Newtonsoft.Json": "12.0.1", - "Nito.AsyncEx.Coordination": "5.0.0", - "PeerTalk": "0.11.3", - "PeterO.Cbor": "3.1.0", - "Portable.BouncyCastle": "1.8.5", - "SharpZipLib": "1.0.0", - "protobuf-net": "2.4.0" - }, - "compile": { - "lib/netstandard2.0/Ipfs.Engine.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Ipfs.Engine.dll": {} - } - }, - "Ipfs.HttpGateway/0.4.0": { - "type": "package", - "dependencies": { - "Ipfs.Core": "0.51.1", - "Ipfs.Engine": "0.9.1" - }, - "compile": { - "lib/netcoreapp2.2/Ipfs.HttpGateway.Views.dll": {}, - "lib/netcoreapp2.2/Ipfs.HttpGateway.dll": {} - }, - "runtime": { - "lib/netcoreapp2.2/Ipfs.HttpGateway.Views.dll": {}, - "lib/netcoreapp2.2/Ipfs.HttpGateway.dll": {} - } - }, - "IPNetwork2/2.1.2": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1" - }, - "compile": { - "lib/netstandard1.3/System.Net.IPNetwork.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Net.IPNetwork.dll": {} - } - }, - "Makaretu.Dns/1.4.1": { - "type": "package", - "dependencies": { - "SimpleBase": "1.3.1" - }, - "compile": { - "lib/netstandard2.0/Makaretu.Dns.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Makaretu.Dns.dll": {} - } - }, - "Makaretu.Dns.Multicast/0.18.0": { - "type": "package", - "dependencies": { - "Common.Logging": "3.4.1", - "IPNetwork2": "2.1.2", - "Makaretu.Dns": "1.3.0", - "Tmds.LibC": "0.2.0" - }, - "compile": { - "lib/netstandard2.0/Makaretu.Dns.Multicast.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Makaretu.Dns.Multicast.dll": {} - } - }, - "Makaretu.Dns.Unicast/0.9.0": { - "type": "package", - "dependencies": { - "Common.Logging": "3.4.1", - "Makaretu.Dns": "1.4.1", - "Nito.AsyncEx": "5.0.0" - }, - "compile": { - "lib/netstandard2.0/Makaretu.Dns.Unicast.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Makaretu.Dns.Unicast.dll": {} - } - }, - "Makaretu.KBucket/0.5.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Makaretu.KBucket.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Makaretu.KBucket.dll": {} - } - }, - "Microsoft.CSharp/4.0.1": { - "type": "package", - "dependencies": { - "System.Collections": "4.0.11", - "System.Diagnostics.Debug": "4.0.11", - "System.Dynamic.Runtime": "4.0.11", - "System.Globalization": "4.0.11", - "System.Linq": "4.1.0", - "System.Linq.Expressions": "4.1.0", - "System.ObjectModel": "4.0.12", - "System.Reflection": "4.1.0", - "System.Reflection.Extensions": "4.0.1", - "System.Reflection.Primitives": "4.0.1", - "System.Reflection.TypeExtensions": "4.1.0", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Runtime.InteropServices": "4.1.0", - "System.Threading": "4.0.11" - }, - "compile": { - "ref/netstandard1.0/Microsoft.CSharp.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Microsoft.CSharp.dll": {} - } - }, - "Microsoft.DotNet.PlatformAbstractions/2.0.4": { - "type": "package", - "dependencies": { - "System.AppContext": "4.1.0", - "System.Collections": "4.0.11", - "System.IO": "4.1.0", - "System.IO.FileSystem": "4.0.1", - "System.Reflection.TypeExtensions": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Runtime.InteropServices": "4.1.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.0.0" - }, - "compile": { - "lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll": {} - } - }, - "Microsoft.Extensions.Caching.Abstractions/2.1.2": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.1.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.Caching.Memory/2.1.2": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "2.1.2", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", - "Microsoft.Extensions.Options": "2.1.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll": {} - } - }, - "Microsoft.Extensions.Configuration/2.1.1": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "2.1.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll": {} - } - }, - "Microsoft.Extensions.Configuration.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.Configuration.Binder/2.0.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.0.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll": {} - } - }, - "Microsoft.Extensions.Configuration.FileExtensions/2.1.1": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.1", - "Microsoft.Extensions.FileProviders.Physical": "2.1.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": {} - } - }, - "Microsoft.Extensions.Configuration.Json/2.1.1": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.1", - "Microsoft.Extensions.Configuration.FileExtensions": "2.1.1", - "Newtonsoft.Json": "11.0.2" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll": {} - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/2.1.1": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.DependencyModel/2.0.4": { - "type": "package", - "dependencies": { - "Microsoft.DotNet.PlatformAbstractions": "2.0.4", - "Newtonsoft.Json": "9.0.1", - "System.Diagnostics.Debug": "4.0.11", - "System.Dynamic.Runtime": "4.0.11", - "System.Linq": "4.1.0" - }, - "compile": { - "lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll": {} - }, - "runtime": { - "lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll": {} - } - }, - "Microsoft.Extensions.FileProviders.Abstractions/2.1.1": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.1.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.FileProviders.Physical/2.1.1": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.1", - "Microsoft.Extensions.FileSystemGlobbing": "2.1.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": {} - } - }, - "Microsoft.Extensions.FileSystemGlobbing/2.1.1": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": {} - } - }, - "Microsoft.Extensions.Logging/2.0.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.0.0", - "Microsoft.Extensions.Logging.Abstractions": "2.0.0", - "Microsoft.Extensions.Options": "2.0.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.dll": {} - } - }, - "Microsoft.Extensions.Logging.Abstractions/2.0.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.Options/2.1.1": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.1", - "Microsoft.Extensions.Primitives": "2.1.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Options.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Options.dll": {} - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions/2.0.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "2.0.0", - "Microsoft.Extensions.Configuration.Binder": "2.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.0.0", - "Microsoft.Extensions.Options": "2.0.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll": {} - } - }, - "Microsoft.Extensions.Primitives/2.2.0": { - "type": "package", - "dependencies": { - "System.Memory": "4.5.1", - "System.Runtime.CompilerServices.Unsafe": "4.5.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll": {} - } - }, - "Microsoft.IO.RecyclableMemoryStream/1.2.2": { - "type": "package", - "compile": { - "lib/netstandard1.4/Microsoft.IO.RecyclableMemoryStream.dll": {} - }, - "runtime": { - "lib/netstandard1.4/Microsoft.IO.RecyclableMemoryStream.dll": {} - } - }, - "Microsoft.NETCore.Platforms/2.1.0": { - "type": "package", - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "Microsoft.NETCore.Targets/1.1.0": { - "type": "package", - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "Microsoft.Win32.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/Microsoft.Win32.Primitives.dll": {} - } - }, - "Microsoft.Win32.Registry/4.0.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.0.1", - "System.Collections": "4.0.11", - "System.Globalization": "4.0.11", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Runtime.Handles": "4.0.1", - "System.Runtime.InteropServices": "4.1.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/Microsoft.Win32.Registry.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/Microsoft.Win32.Registry.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "MongoDB.Bson/2.7.0": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.Collections.NonGeneric": "4.0.1", - "System.Diagnostics.Process": "4.1.0", - "System.Dynamic.Runtime": "4.0.11", - "System.Reflection.Emit.Lightweight": "4.0.1" - }, - "compile": { - "lib/netstandard1.5/MongoDB.Bson.dll": {} - }, - "runtime": { - "lib/netstandard1.5/MongoDB.Bson.dll": {} - } - }, - "Multiformats.Base/2.0.1": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.Runtime.Numerics": "4.3.0" - }, - "compile": { - "lib/netstandard1.6/Multiformats.Base.dll": {} - }, - "runtime": { - "lib/netstandard1.6/Multiformats.Base.dll": {} - } - }, - "Multiformats.Hash/1.5.0": { - "type": "package", - "dependencies": { - "BinaryEncoding": "1.4.0", - "Multiformats.Base": "2.0.1", - "Portable.BouncyCastle": "1.8.5", - "System.Composition": "1.2.0", - "murmurhash": "1.0.2" - }, - "compile": { - "lib/netstandard2.0/Multiformats.Hash.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Multiformats.Hash.dll": {} - } - }, - "murmurhash/1.0.2": { - "type": "package", - "compile": { - "lib/netstandard1.4/MurmurHash.dll": {} - }, - "runtime": { - "lib/netstandard1.4/MurmurHash.dll": {} - } - }, - "Nethereum.Hex/3.3.0": { - "type": "package", - "dependencies": { - "Newtonsoft.Json": "[10.0.3, 13.0.0)", - "Portable.BouncyCastle": "1.8.2" - }, - "compile": { - "lib/netcoreapp2.1/Nethereum.Hex.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/Nethereum.Hex.dll": {} - } - }, - "Nethereum.RLP/3.3.0": { - "type": "package", - "dependencies": { - "Nethereum.Hex": "3.3.0", - "Newtonsoft.Json": "[10.0.3, 13.0.0)", - "Portable.BouncyCastle": "1.8.2" - }, - "compile": { - "lib/netcoreapp2.1/Nethereum.RLP.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/Nethereum.RLP.dll": {} - } - }, - "NETStandard.Library/1.6.1": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" - } - }, - "Newtonsoft.Json/12.0.2": { - "type": "package", - "compile": { - "lib/netstandard2.0/Newtonsoft.Json.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Newtonsoft.Json.dll": {} - } - }, - "Nito.AsyncEx/5.0.0": { - "type": "package", - "dependencies": { - "Nito.AsyncEx.Context": "5.0.0", - "Nito.AsyncEx.Coordination": "5.0.0", - "Nito.AsyncEx.Interop.WaitHandles": "5.0.0", - "Nito.AsyncEx.Oop": "5.0.0", - "Nito.AsyncEx.Tasks": "5.0.0", - "Nito.Cancellation": "1.0.5" - } - }, - "Nito.AsyncEx.Context/5.0.0": { - "type": "package", - "dependencies": { - "Nito.AsyncEx.Tasks": "5.0.0" - }, - "compile": { - "lib/netstandard2.0/Nito.AsyncEx.Context.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.AsyncEx.Context.dll": {} - } - }, - "Nito.AsyncEx.Coordination/5.0.0": { - "type": "package", - "dependencies": { - "Nito.AsyncEx.Tasks": "5.0.0", - "Nito.Collections.Deque": "1.0.4", - "Nito.Disposables": "2.0.0" - }, - "compile": { - "lib/netstandard2.0/Nito.AsyncEx.Coordination.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.AsyncEx.Coordination.dll": {} - } - }, - "Nito.AsyncEx.Interop.WaitHandles/5.0.0": { - "type": "package", - "dependencies": { - "Nito.AsyncEx.Tasks": "5.0.0" - }, - "compile": { - "lib/netstandard2.0/Nito.AsyncEx.Interop.WaitHandles.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.AsyncEx.Interop.WaitHandles.dll": {} - } - }, - "Nito.AsyncEx.Oop/5.0.0": { - "type": "package", - "dependencies": { - "Nito.AsyncEx.Coordination": "5.0.0" - }, - "compile": { - "lib/netstandard2.0/Nito.AsyncEx.Oop.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.AsyncEx.Oop.dll": {} - } - }, - "Nito.AsyncEx.Tasks/5.0.0": { - "type": "package", - "dependencies": { - "Nito.Disposables": "2.0.0" - }, - "compile": { - "lib/netstandard2.0/Nito.AsyncEx.Tasks.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.AsyncEx.Tasks.dll": {} - } - }, - "Nito.Cancellation/1.0.5": { - "type": "package", - "dependencies": { - "Nito.Disposables": "1.2.3" - }, - "compile": { - "lib/netstandard2.0/Nito.Cancellation.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.Cancellation.dll": {} - } - }, - "Nito.Collections.Deque/1.0.4": { - "type": "package", - "compile": { - "lib/netstandard2.0/Nito.Collections.Deque.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.Collections.Deque.dll": {} - } - }, - "Nito.Disposables/2.0.0": { - "type": "package", - "dependencies": { - "System.Collections.Immutable": "1.4.0" - }, - "compile": { - "lib/netstandard2.0/Nito.Disposables.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.Disposables.dll": {} - } - }, - "NLog/4.6.5": { - "type": "package", - "compile": { - "lib/netstandard2.0/NLog.dll": {} - }, - "runtime": { - "lib/netstandard2.0/NLog.dll": {} - } - }, - "NLog.StructuredLogging.Json/4.0.0": { - "type": "package", - "dependencies": { - "NLog": "4.5.0", - "Newtonsoft.Json": "9.0.1" - }, - "compile": { - "lib/netstandard2.0/NLog.StructuredLogging.Json.dll": {} - }, - "runtime": { - "lib/netstandard2.0/NLog.StructuredLogging.Json.dll": {} - } - }, - "NLog.Targets.Seq/1.1.0": { - "type": "package", - "dependencies": { - "NLog": "4.5.11" - }, - "compile": { - "lib/netstandard2.0/NLog.Targets.Seq.dll": {} - }, - "runtime": { - "lib/netstandard2.0/NLog.Targets.Seq.dll": {} - } - }, - "NLog.Targets.Syslog/5.1.0": { - "type": "package", - "dependencies": { - "NLog": "4.5.4" - }, - "compile": { - "lib/netstandard2.0/NLog.Targets.Syslog.dll": {} - }, - "runtime": { - "lib/netstandard2.0/NLog.Targets.Syslog.dll": {} - } - }, - "PeerTalk/0.11.3": { - "type": "package", - "dependencies": { - "Ipfs.Core": "0.50.1", - "Makaretu.Dns.Multicast": "0.18.0", - "Makaretu.KBucket": "0.5.0", - "Nito.AsyncEx": "5.0.0", - "Portable.BouncyCastle": "1.8.5", - "System.Threading.Tasks.Dataflow": "4.9.0", - "protobuf-net": "2.4.0", - "semver": "2.0.4" - }, - "compile": { - "lib/netstandard2.0/PeerTalk.dll": {} - }, - "runtime": { - "lib/netstandard2.0/PeerTalk.dll": {} - } - }, - "PeterO.Cbor/3.1.0": { - "type": "package", - "dependencies": { - "PeterO.Numbers": "1.0.2" - }, - "compile": { - "lib/netstandard1.0/CBOR.dll": {} - }, - "runtime": { - "lib/netstandard1.0/CBOR.dll": {} - } - }, - "PeterO.Numbers/1.0.2": { - "type": "package", - "compile": { - "lib/netstandard1.0/Numbers.dll": {} - }, - "runtime": { - "lib/netstandard1.0/Numbers.dll": {} - } - }, - "Polly/7.1.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Polly.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Polly.dll": {} - } - }, - "Portable.BouncyCastle/1.8.5": { - "type": "package", - "compile": { - "lib/netstandard2.0/BouncyCastle.Crypto.dll": {} - }, - "runtime": { - "lib/netstandard2.0/BouncyCastle.Crypto.dll": {} - } - }, - "protobuf-net/2.4.0": { - "type": "package", - "dependencies": { - "System.ServiceModel.Primitives": "4.5.3" - }, - "compile": { - "lib/netcoreapp2.1/protobuf-net.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/protobuf-net.dll": {} - } - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/debian.8-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "debian.8-x64" - } - } - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/fedora.23-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "fedora.23-x64" - } - } - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/fedora.24-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "fedora.24-x64" - } - } - }, - "runtime.native.System/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.IO.Compression/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Net.Http/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Net.Security/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Security.Cryptography.Apple/4.3.1": { - "type": "package", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.1" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/opensuse.13.2-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "opensuse.13.2-x64" - } - } - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/opensuse.42.1-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "opensuse.42.1-x64" - } - } - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.1": { - "type": "package", - "runtimeTargets": { - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.Apple.dylib": { - "assetType": "native", - "rid": "osx.10.10-x64" - } - } - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.OpenSsl.dylib": { - "assetType": "native", - "rid": "osx.10.10-x64" - } - } - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/rhel.7-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "rhel.7-x64" - } - } - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/ubuntu.14.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "ubuntu.14.04-x64" - } - } - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/ubuntu.16.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "ubuntu.16.04-x64" - } - } - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/ubuntu.16.10-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "ubuntu.16.10-x64" - } - } - }, - "Semver/2.0.4": { - "type": "package", - "dependencies": { - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Runtime.InteropServices": "4.1.0", - "System.Text.RegularExpressions": "4.1.0" - }, - "compile": { - "lib/netstandard1.1/Semver.dll": {} - }, - "runtime": { - "lib/netstandard1.1/Semver.dll": {} - } - }, - "Serilog/2.6.0": { - "type": "package", - "dependencies": { - "Microsoft.CSharp": "4.0.1", - "System.Collections": "4.0.11", - "System.Collections.NonGeneric": "4.0.1", - "System.Dynamic.Runtime": "4.0.11", - "System.Globalization": "4.0.11", - "System.Linq": "4.1.0", - "System.Reflection": "4.1.0", - "System.Reflection.Extensions": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Text.RegularExpressions": "4.1.0", - "System.Threading": "4.0.11" - }, - "compile": { - "lib/netstandard1.3/Serilog.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Serilog.dll": {} - } - }, - "Serilog.Enrichers.Environment/2.1.3": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1", - "Serilog": "2.3.0" - }, - "compile": { - "lib/netstandard1.5/Serilog.Enrichers.Environment.dll": {} - }, - "runtime": { - "lib/netstandard1.5/Serilog.Enrichers.Environment.dll": {} - } - }, - "Serilog.Enrichers.Thread/3.1.0": { - "type": "package", - "dependencies": { - "Serilog": "2.3.0" - }, - "compile": { - "lib/netstandard2.0/Serilog.Enrichers.Thread.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Serilog.Enrichers.Thread.dll": {} - } - }, - "Serilog.Extensions.Logging/2.0.4": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Logging": "2.0.0", - "Serilog": "2.3.0" - }, - "compile": { - "lib/netstandard2.0/Serilog.Extensions.Logging.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Serilog.Extensions.Logging.dll": {} - } - }, - "Serilog.Settings.Configuration/3.1.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.DependencyModel": "2.0.4", - "Microsoft.Extensions.Options.ConfigurationExtensions": "2.0.0", - "Serilog": "2.6.0" - }, - "compile": { - "lib/netstandard2.0/Serilog.Settings.Configuration.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Serilog.Settings.Configuration.dll": {} - } - }, - "Serilog.Sinks.Console/3.1.1": { - "type": "package", - "dependencies": { - "Serilog": "2.5.0", - "System.Console": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0" - }, - "compile": { - "lib/netcoreapp1.1/Serilog.Sinks.Console.dll": {} - }, - "runtime": { - "lib/netcoreapp1.1/Serilog.Sinks.Console.dll": {} - } - }, - "Serilog.Sinks.File/4.0.0": { - "type": "package", - "dependencies": { - "Serilog": "2.5.0", - "System.IO": "4.1.0", - "System.IO.FileSystem": "4.0.1", - "System.IO.FileSystem.Primitives": "4.0.1", - "System.Runtime.InteropServices": "4.1.0", - "System.Text.Encoding.Extensions": "4.0.11", - "System.Threading": "4.0.11", - "System.Threading.Timer": "4.0.1" - }, - "compile": { - "lib/netstandard1.3/Serilog.Sinks.File.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Serilog.Sinks.File.dll": {} - } - }, - "SharpRepository.Ioc.Autofac/2.0.4.2": { - "type": "package", - "dependencies": { - "Autofac": "4.8.1", - "NETStandard.Library": "1.6.1", - "SharpRepository.Repository": "2.0.4.6" - }, - "compile": { - "lib/netstandard1.3/SharpRepository.Ioc.Autofac.dll": {} - }, - "runtime": { - "lib/netstandard1.3/SharpRepository.Ioc.Autofac.dll": {} - } - }, - "SharpRepository.Repository/2.0.4.6": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Caching.Memory": "2.1.2", - "Microsoft.Extensions.Configuration.Json": "2.1.1", - "System.Linq.Queryable": "4.3.0", - "System.Reflection.TypeExtensions": "4.5.1", - "System.Security.Cryptography.Algorithms": "4.3.1" - }, - "compile": { - "lib/netstandard2.0/SharpRepository.Repository.dll": {} - }, - "runtime": { - "lib/netstandard2.0/SharpRepository.Repository.dll": {} - }, - "contentFiles": { - "contentFiles/any/any/_._": { - "buildAction": "None", - "codeLanguage": "any", - "copyToOutput": false - } - } - }, - "SharpRepository.XmlRepository/2.0.1-alpha3": { - "type": "package", - "dependencies": { - "SharpRepository.Repository": "2.0.1-beta1" - }, - "compile": { - "lib/netstandard2.0/SharpRepository.XmlRepository.dll": {} - }, - "runtime": { - "lib/netstandard2.0/SharpRepository.XmlRepository.dll": {} - }, - "contentFiles": { - "contentFiles/any/netstandard2.0/repository.xml.json": { - "buildAction": "Content", - "codeLanguage": "any", - "copyToOutput": false - } - } - }, - "SharpZipLib/1.0.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/ICSharpCode.SharpZipLib.dll": {} - }, - "runtime": { - "lib/netstandard2.0/ICSharpCode.SharpZipLib.dll": {} - } - }, - "SimpleBase/1.3.1": { - "type": "package", - "compile": { - "lib/netstandard1.3/SimpleBase.dll": {} - }, - "runtime": { - "lib/netstandard1.3/SimpleBase.dll": {} - } - }, - "System.AppContext/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/System.AppContext.dll": {} - }, - "runtime": { - "lib/netstandard1.6/System.AppContext.dll": {} - } - }, - "System.Buffers/4.5.0": { - "type": "package", - "compile": { - "ref/netcoreapp2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/_._": {} - } - }, - "System.Collections/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Collections.dll": {} - } - }, - "System.Collections.Concurrent/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Collections.Concurrent.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Collections.Concurrent.dll": {} - } - }, - "System.Collections.Immutable/1.5.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Collections.Immutable.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Collections.Immutable.dll": {} - } - }, - "System.Collections.NonGeneric/4.0.1": { - "type": "package", - "dependencies": { - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" - }, - "compile": { - "ref/netstandard1.3/System.Collections.NonGeneric.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Collections.NonGeneric.dll": {} - } - }, - "System.ComponentModel/4.0.1": { - "type": "package", - "dependencies": { - "System.Runtime": "4.1.0" - }, - "compile": { - "ref/netstandard1.0/System.ComponentModel.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.ComponentModel.dll": {} - } - }, - "System.Composition/1.2.0": { - "type": "package", - "dependencies": { - "System.Composition.AttributedModel": "1.2.0", - "System.Composition.Convention": "1.2.0", - "System.Composition.Hosting": "1.2.0", - "System.Composition.Runtime": "1.2.0", - "System.Composition.TypedParts": "1.2.0" - } - }, - "System.Composition.AttributedModel/1.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Composition.AttributedModel.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Composition.AttributedModel.dll": {} - } - }, - "System.Composition.Convention/1.2.0": { - "type": "package", - "dependencies": { - "System.Composition.AttributedModel": "1.2.0" - }, - "compile": { - "lib/netstandard2.0/System.Composition.Convention.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Composition.Convention.dll": {} - } - }, - "System.Composition.Hosting/1.2.0": { - "type": "package", - "dependencies": { - "System.Composition.Runtime": "1.2.0" - }, - "compile": { - "lib/netstandard2.0/System.Composition.Hosting.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Composition.Hosting.dll": {} - } - }, - "System.Composition.Runtime/1.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Composition.Runtime.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Composition.Runtime.dll": {} - } - }, - "System.Composition.TypedParts/1.2.0": { - "type": "package", - "dependencies": { - "System.Composition.AttributedModel": "1.2.0", - "System.Composition.Hosting": "1.2.0", - "System.Composition.Runtime": "1.2.0" - }, - "compile": { - "lib/netstandard2.0/System.Composition.TypedParts.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Composition.TypedParts.dll": {} - } - }, - "System.Console/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Console.dll": {} - } - }, - "System.Diagnostics.Contracts/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Diagnostics.Contracts.dll": {} - }, - "runtime": { - "lib/netstandard1.0/System.Diagnostics.Contracts.dll": {} - } - }, - "System.Diagnostics.Debug/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Diagnostics.Debug.dll": {} - } - }, - "System.Diagnostics.DiagnosticSource/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "lib/netstandard1.3/_._": {} - }, - "runtime": { - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": {} - } - }, - "System.Diagnostics.Process/4.1.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.0.1", - "Microsoft.Win32.Primitives": "4.0.1", - "Microsoft.Win32.Registry": "4.0.0", - "System.Collections": "4.0.11", - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.IO": "4.1.0", - "System.IO.FileSystem": "4.0.1", - "System.IO.FileSystem.Primitives": "4.0.1", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Runtime.Handles": "4.0.1", - "System.Runtime.InteropServices": "4.1.0", - "System.Text.Encoding": "4.0.11", - "System.Text.Encoding.Extensions": "4.0.11", - "System.Threading": "4.0.11", - "System.Threading.Tasks": "4.0.11", - "System.Threading.Thread": "4.0.0", - "System.Threading.ThreadPool": "4.0.10", - "runtime.native.System": "4.0.0" - }, - "compile": { - "ref/netstandard1.4/System.Diagnostics.Process.dll": {} - }, - "runtimeTargets": { - "runtimes/linux/lib/netstandard1.4/System.Diagnostics.Process.dll": { - "assetType": "runtime", - "rid": "linux" - }, - "runtimes/osx/lib/netstandard1.4/System.Diagnostics.Process.dll": { - "assetType": "runtime", - "rid": "osx" - }, - "runtimes/win/lib/netstandard1.4/System.Diagnostics.Process.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Diagnostics.StackTrace/4.3.0": { - "type": "package", - "dependencies": { - "System.IO.FileSystem": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Metadata": "1.4.1", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Diagnostics.StackTrace.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Diagnostics.StackTrace.dll": {} - } - }, - "System.Diagnostics.Tools/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Diagnostics.Tools.dll": {} - } - }, - "System.Diagnostics.Tracing/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.Diagnostics.Tracing.dll": {} - } - }, - "System.Dynamic.Runtime/4.0.11": { - "type": "package", - "dependencies": { - "System.Collections": "4.0.11", - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.Linq": "4.1.0", - "System.Linq.Expressions": "4.1.0", - "System.ObjectModel": "4.0.12", - "System.Reflection": "4.1.0", - "System.Reflection.Emit": "4.0.1", - "System.Reflection.Emit.ILGeneration": "4.0.1", - "System.Reflection.Primitives": "4.0.1", - "System.Reflection.TypeExtensions": "4.1.0", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" - }, - "compile": { - "ref/netstandard1.3/System.Dynamic.Runtime.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Dynamic.Runtime.dll": {} - } - }, - "System.Globalization/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Globalization.dll": {} - } - }, - "System.Globalization.Calendars/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Globalization.Calendars.dll": {} - } - }, - "System.Globalization.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Globalization.Extensions.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Globalization.Extensions.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Globalization.Extensions.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.IO/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.IO.dll": {} - } - }, - "System.IO.Abstractions/6.0.3": { - "type": "package", - "dependencies": { - "System.IO.FileSystem.AccessControl": "4.5.0" - }, - "compile": { - "lib/netcoreapp2.0/System.IO.Abstractions.dll": {} - }, - "runtime": { - "lib/netcoreapp2.0/System.IO.Abstractions.dll": {} - } - }, - "System.IO.Compression/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.Compression.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.IO.Compression.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.IO.Compression.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.IO.Compression.ZipFile/4.3.0": { - "type": "package", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.Compression.ZipFile.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.IO.Compression.ZipFile.dll": {} - } - }, - "System.IO.FileSystem/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.FileSystem.dll": {} - } - }, - "System.IO.FileSystem.AccessControl/4.5.0": { - "type": "package", - "dependencies": { - "System.Security.AccessControl": "4.5.0", - "System.Security.Principal.Windows": "4.5.0" - }, - "compile": { - "ref/netstandard2.0/System.IO.FileSystem.AccessControl.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.IO.FileSystem.AccessControl.dll": {} - }, - "runtimeTargets": { - "runtimes/win/lib/netstandard2.0/System.IO.FileSystem.AccessControl.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.IO.FileSystem.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.FileSystem.Primitives.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.IO.FileSystem.Primitives.dll": {} - } - }, - "System.Linq/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/System.Linq.dll": {} - }, - "runtime": { - "lib/netstandard1.6/System.Linq.dll": {} - } - }, - "System.Linq.Expressions/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/System.Linq.Expressions.dll": {} - }, - "runtime": { - "lib/netstandard1.6/System.Linq.Expressions.dll": {} - } - }, - "System.Linq.Queryable/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Linq.Queryable.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Linq.Queryable.dll": {} - } - }, - "System.Memory/4.5.2": { - "type": "package", - "compile": { - "ref/netcoreapp2.1/_._": {} - }, - "runtime": { - "lib/netcoreapp2.1/_._": {} - } - }, - "System.Net.Http/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.Http.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Net.Http.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Net.Http.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Net.NameResolution/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Principal.Windows": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.NameResolution.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Net.NameResolution.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Net.NameResolution.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Net.NetworkInformation/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Principal.Windows": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Overlapped": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Thread": "4.3.0", - "System.Threading.ThreadPool": "4.3.0", - "runtime.native.System": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.NetworkInformation.dll": {} - }, - "runtimeTargets": { - "runtimes/linux/lib/netstandard1.3/System.Net.NetworkInformation.dll": { - "assetType": "runtime", - "rid": "linux" - }, - "runtimes/osx/lib/netstandard1.3/System.Net.NetworkInformation.dll": { - "assetType": "runtime", - "rid": "osx" - }, - "runtimes/win/lib/netstandard1.3/System.Net.NetworkInformation.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Net.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.Primitives.dll": {} - } - }, - "System.Net.Security/4.3.2": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Claims": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Security.Principal": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.ThreadPool": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Security": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - }, - "compile": { - "ref/netstandard1.3/System.Net.Security.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Net.Security.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Net.Security.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Net.Sockets/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.Sockets.dll": {} - } - }, - "System.ObjectModel/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.ObjectModel.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.ObjectModel.dll": {} - } - }, - "System.Private.ServiceModel/4.5.3": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0", - "System.Reflection.DispatchProxy": "4.5.0", - "System.Security.Principal.Windows": "4.5.0" - }, - "compile": { - "ref/netstandard/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard2.0/System.Private.ServiceModel.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard2.0/System.Private.ServiceModel.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Reactive/4.1.6": { - "type": "package", - "dependencies": { - "System.Runtime.InteropServices.WindowsRuntime": "4.3.0", - "System.Threading.Tasks.Extensions": "4.5.2" - }, - "compile": { - "lib/netstandard2.0/System.Reactive.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Reactive.dll": {} - } - }, - "System.Reflection/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.Reflection.dll": {} - } - }, - "System.Reflection.DispatchProxy/4.5.0": { - "type": "package", - "compile": { - "ref/netstandard2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/System.Reflection.DispatchProxy.dll": {} - } - }, - "System.Reflection.Emit/4.3.0": { - "type": "package", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.1/_._": {} - }, - "runtime": { - "lib/netstandard1.3/System.Reflection.Emit.dll": {} - } - }, - "System.Reflection.Emit.ILGeneration/4.3.0": { - "type": "package", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Reflection.Emit.ILGeneration.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Reflection.Emit.ILGeneration.dll": {} - } - }, - "System.Reflection.Emit.Lightweight/4.3.0": { - "type": "package", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Reflection.Emit.Lightweight.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Reflection.Emit.Lightweight.dll": {} - } - }, - "System.Reflection.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Reflection.Extensions.dll": {} - } - }, - "System.Reflection.Metadata/1.4.1": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Collections.Immutable": "1.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.Linq": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "lib/netstandard1.1/_._": {} - }, - "runtime": { - "lib/netstandard1.1/System.Reflection.Metadata.dll": {} - } - }, - "System.Reflection.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Reflection.Primitives.dll": {} - } - }, - "System.Reflection.TypeExtensions/4.5.1": { - "type": "package", - "compile": { - "ref/netcoreapp2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/_._": {} - } - }, - "System.Resources.ResourceManager/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Resources.ResourceManager.dll": {} - } - }, - "System.Runtime/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "ref/netstandard1.5/System.Runtime.dll": {} - } - }, - "System.Runtime.CompilerServices.Unsafe/4.5.2": { - "type": "package", - "compile": { - "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": {} - }, - "runtime": { - "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll": {} - } - }, - "System.Runtime.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.Runtime.Extensions.dll": {} - } - }, - "System.Runtime.Handles/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Runtime.Handles.dll": {} - } - }, - "System.Runtime.InteropServices/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compile": { - "ref/netcoreapp1.1/System.Runtime.InteropServices.dll": {} - } - }, - "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { - "type": "package", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - }, - "compile": { - "ref/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": {} - }, - "runtime": { - "lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Runtime.InteropServices.WindowsRuntime/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Runtime.InteropServices.WindowsRuntime.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Runtime.InteropServices.WindowsRuntime.dll": {} - } - }, - "System.Runtime.Numerics/4.3.0": { - "type": "package", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - }, - "compile": { - "ref/netstandard1.1/System.Runtime.Numerics.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Runtime.Numerics.dll": {} - } - }, - "System.Security.AccessControl/4.5.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "2.0.0", - "System.Security.Principal.Windows": "4.5.0" - }, - "compile": { - "ref/netstandard2.0/System.Security.AccessControl.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Security.AccessControl.dll": {} - }, - "runtimeTargets": { - "runtimes/win/lib/netcoreapp2.0/System.Security.AccessControl.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Claims/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Security.Principal": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtime": { - "lib/netstandard1.3/System.Security.Claims.dll": {} - } - }, - "System.Security.Cryptography.Algorithms/4.3.1": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.1", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - }, - "compile": { - "ref/netstandard1.6/System.Security.Cryptography.Algorithms.dll": {} - }, - "runtimeTargets": { - "runtimes/osx/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { - "assetType": "runtime", - "rid": "osx" - }, - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.Cng/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Cng.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Cng.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.Csp/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Csp.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Csp.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.Encoding/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Security.Cryptography.Encoding.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/_._": {} - }, - "runtime": { - "lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll": { - "assetType": "runtime", - "rid": "unix" - } - } - }, - "System.Security.Cryptography.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Security.Cryptography.Primitives.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Security.Cryptography.Primitives.dll": {} - } - }, - "System.Security.Cryptography.X509Certificates/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Principal/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Security.Principal.dll": {} - }, - "runtime": { - "lib/netstandard1.0/System.Security.Principal.dll": {} - } - }, - "System.Security.Principal.Windows/4.5.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "2.0.0" - }, - "compile": { - "ref/netstandard2.0/System.Security.Principal.Windows.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Security.Principal.Windows.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netcoreapp2.0/System.Security.Principal.Windows.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netcoreapp2.0/System.Security.Principal.Windows.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.ServiceModel.Primitives/4.5.3": { - "type": "package", - "dependencies": { - "System.Private.ServiceModel": "4.5.3" - }, - "compile": { - "ref/netstandard2.0/System.ServiceModel.Primitives.dll": {}, - "ref/netstandard2.0/System.ServiceModel.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.ServiceModel.Primitives.dll": {}, - "lib/netstandard2.0/System.ServiceModel.dll": {} - } - }, - "System.Text.Encoding/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Text.Encoding.dll": {} - } - }, - "System.Text.Encoding.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Text.Encoding.Extensions.dll": {} - } - }, - "System.Text.RegularExpressions/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netcoreapp1.1/System.Text.RegularExpressions.dll": {} - }, - "runtime": { - "lib/netstandard1.6/System.Text.RegularExpressions.dll": {} - } - }, - "System.Threading/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Threading.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Threading.dll": {} - } - }, - "System.Threading.Overlapped/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Threading.Overlapped.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Threading.Overlapped.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Threading.Tasks/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Threading.Tasks.dll": {} - } - }, - "System.Threading.Tasks.Dataflow/4.9.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Threading.Tasks.Dataflow.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Threading.Tasks.Dataflow.dll": {} - } - }, - "System.Threading.Tasks.Extensions/4.5.2": { - "type": "package", - "compile": { - "ref/netcoreapp2.1/_._": {} - }, - "runtime": { - "lib/netcoreapp2.1/_._": {} - } - }, - "System.Threading.Thread/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtime": { - "lib/netstandard1.3/System.Threading.Thread.dll": {} - } - }, - "System.Threading.ThreadPool/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtime": { - "lib/netstandard1.3/System.Threading.ThreadPool.dll": {} - } - }, - "System.Threading.Timer/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.2/System.Threading.Timer.dll": {} - } - }, - "System.Xml.ReaderWriter/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Xml.ReaderWriter.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Xml.ReaderWriter.dll": {} - } - }, - "System.Xml.XDocument/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Xml.XDocument.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Xml.XDocument.dll": {} - } - }, - "Tmds.LibC/0.2.0": { - "type": "package", - "compile": { - "ref/netstandard2.0/Tmds.LibC.dll": {} - }, - "runtimeTargets": { - "runtimes/linux-arm/lib/netstandard2.0/Tmds.LibC.dll": { - "assetType": "runtime", - "rid": "linux-arm" - }, - "runtimes/linux-arm64/lib/netstandard2.0/Tmds.LibC.dll": { - "assetType": "runtime", - "rid": "linux-arm64" - }, - "runtimes/linux-x64/lib/netstandard2.0/Tmds.LibC.dll": { - "assetType": "runtime", - "rid": "linux-x64" - } - } - }, - "Catalyst.Abstractions/0.1.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "Catalyst.Protocol": "1.0.0", - "DnsClient": "1.2.0", - "DotNetty.Transport": "0.6.0", - "Google.Protobuf": "3.9.1", - "Ipfs.HttpGateway": "0.4.0", - "Microsoft.Extensions.Configuration.Abstractions": "2.2.0", - "Multiformats.Hash": "1.5.0", - "Nethermind.Dirichlet.Numerics": "1.0.0", - "Nethermind.Evm": "1.0.0", - "SharpRepository.Repository": "2.0.4.6", - "System.IO.Abstractions": "6.0.3", - "System.Reactive": "4.1.6" - }, - "compile": { - "bin/placeholder/Catalyst.Abstractions.dll": {} - }, - "runtime": { - "bin/placeholder/Catalyst.Abstractions.dll": {} - } - }, - "Catalyst.Protocol/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "Dawn.Guard": "1.9.0", - "Google.Protobuf": "3.9.1" - }, - "compile": { - "bin/placeholder/Catalyst.Protocol.dll": {} - }, - "runtime": { - "bin/placeholder/Catalyst.Protocol.dll": {} - } - }, - "Nethermind.Core/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "Extensions.Data.xxHash.core20": "1.0.2.1", - "Microsoft.IO.RecyclableMemoryStream": "1.2.2", - "Nethermind.Dirichlet.Numerics": "1.0.0", - "Nethermind.HashLib": "1.0.0", - "Nethermind.Logging": "1.0.0", - "Nethermind.Secp256k1": "1.0.0", - "Newtonsoft.Json": "12.0.2" - }, - "compile": { - "bin/placeholder/Nethermind.Core.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Core.dll": {} - } - }, - "Nethermind.Dirichlet.Numerics/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "compile": { - "bin/placeholder/Nethermind.Dirichlet.Numerics.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Dirichlet.Numerics.dll": {} - } - }, - "Nethermind.Evm/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "Microsoft.IO.RecyclableMemoryStream": "1.2.2", - "Nethermind.Core": "1.0.0", - "Nethermind.Dirichlet.Numerics": "1.0.0", - "Nethermind.Store": "1.0.0", - "System.Buffers": "4.5.0" - }, - "compile": { - "bin/placeholder/Nethermind.Evm.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Evm.dll": {} - } - }, - "Nethermind.HashLib/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "System.Security.Cryptography.Algorithms": "4.3.1" - }, - "compile": { - "bin/placeholder/Nethermind.HashLib.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.HashLib.dll": {} - } - }, - "Nethermind.Logging/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "NLog": "4.6.5", - "NLog.StructuredLogging.Json": "4.0.0", - "NLog.Targets.Seq": "1.1.0", - "NLog.Targets.Syslog": "5.1.0" - }, - "compile": { - "bin/placeholder/Nethermind.Logging.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Logging.dll": {} - } - }, - "Nethermind.Secp256k1/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "compile": { - "bin/placeholder/Nethermind.Secp256k1.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Secp256k1.dll": {} - } - }, - "Nethermind.Store/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "Nethermind.Core": "1.0.0" - }, - "compile": { - "bin/placeholder/Nethermind.Store.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Store.dll": {} - } - } - } - }, - "libraries": { - "Autofac/4.8.1": { - "sha512": "aIT9rupCOdab5RMfxvWTBmOxGU77tLqmvSF4V89SzV6oQcJrtuKw/Xp55xy9EijSktbMka55SbroAPOyT+lziw==", - "type": "package", - "path": "autofac/4.8.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "autofac.4.8.1.nupkg.sha512", - "autofac.nuspec", - "lib/net45/Autofac.dll", - "lib/net45/Autofac.xml", - "lib/netstandard1.1/Autofac.dll", - "lib/netstandard1.1/Autofac.xml" - ] - }, - "Autofac.Configuration/4.1.0": { - "sha512": "0RBau4Z2V7BYkU3m62cMCOzKjPFakSDQ91aAvLxo/IeafS+2SdRh50B0dVpLYyOfk6JgX63eWvJJXOQ/VT/7Kw==", - "type": "package", - "path": "autofac.configuration/4.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "autofac.configuration.4.1.0.nupkg.sha512", - "autofac.configuration.nuspec", - "lib/net45/Autofac.Configuration.dll", - "lib/net45/Autofac.Configuration.pdb", - "lib/net45/Autofac.Configuration.xml", - "lib/netstandard1.3/Autofac.Configuration.dll", - "lib/netstandard1.3/Autofac.Configuration.pdb", - "lib/netstandard1.3/Autofac.Configuration.xml", - "lib/netstandard2.0/Autofac.Configuration.dll", - "lib/netstandard2.0/Autofac.Configuration.pdb", - "lib/netstandard2.0/Autofac.Configuration.xml" - ] - }, - "AutofacSerilogIntegration/2.0.0": { - "sha512": "m8IjK01G1WUPgj6hEhE5mXjnBc6q+gK39vdFEFb4fzJ4bYOQv3m0pf1Z0Sg6DninRzm/NR9zGjLuyiJ8ixsoWA==", - "type": "package", - "path": "autofacserilogintegration/2.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "autofacserilogintegration.2.0.0.nupkg.sha512", - "autofacserilogintegration.nuspec", - "lib/net45/AutofacSerilogIntegration.dll", - "lib/netstandard1.1/AutofacSerilogIntegration.dll" - ] - }, - "BinaryEncoding/1.4.0": { - "sha512": "1cnkP90c+zNcRyabjKSA3VYJvpYfkGEpXeekfF8KdTFo3VyUUFOioAsANbG8nsMyedGcmUOqHWd1d3fOXke4VA==", - "type": "package", - "path": "binaryencoding/1.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "binaryencoding.1.4.0.nupkg.sha512", - "binaryencoding.nuspec", - "lib/net452/BinaryEncoding.dll", - "lib/net452/BinaryEncoding.runtimeconfig.json", - "lib/netstandard1.1/BinaryEncoding.dll", - "lib/netstandard1.1/BinaryEncoding.runtimeconfig.json" - ] - }, - "Common.Logging/3.4.1": { - "sha512": "5eZ/vgEOqzLg4PypZqnJ+wMhhgHyckicbZY4iDxqQ4FtOz0CpdYZ0xQ78aszMzeAJZiLLb5VdR9tPfunVQLz6g==", - "type": "package", - "path": "common.logging/3.4.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "common.logging.3.4.1.nupkg.sha512", - "common.logging.nuspec", - "lib/net35/Common.Logging.dll", - "lib/net35/Common.Logging.pdb", - "lib/net35/Common.Logging.xml", - "lib/net40/Common.Logging.dll", - "lib/net40/Common.Logging.pdb", - "lib/net40/Common.Logging.xml", - "lib/netstandard1.3/Common.Logging.dll", - "lib/netstandard1.3/Common.Logging.pdb", - "lib/netstandard1.3/Common.Logging.xml" - ] - }, - "Common.Logging.Core/3.4.1": { - "sha512": "wLHldZHvxsSD6Ahonfj00/SkfHfKqO+YT6jsUwVm8Rch1REL9IArHAcSLXxYxYfu5/4ydGtmXvOtaH3AkVPu0A==", - "type": "package", - "path": "common.logging.core/3.4.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "common.logging.core.3.4.1.nupkg.sha512", - "common.logging.core.nuspec", - "lib/net40/Common.Logging.Core.XML", - "lib/net40/Common.Logging.Core.dll", - "lib/net40/Common.Logging.Core.pdb", - "lib/netstandard1.0/Common.Logging.Core.dll", - "lib/netstandard1.0/Common.Logging.Core.pdb", - "lib/netstandard1.0/Common.Logging.Core.xml", - "lib/portable-win+net40+sl40+wp7+wpa81/Common.Logging.Core.XML", - "lib/portable-win+net40+sl40+wp7+wpa81/Common.Logging.Core.dll", - "lib/portable-win+net40+sl40+wp7+wpa81/Common.Logging.Core.pdb" - ] - }, - "Dawn.Guard/1.9.0": { - "sha512": "xqenw7h4BHYMlCK9IeMNHa+db+kfjvnfGrqABHnNT053EvE6rYQS545Q+wTEU5sBZiEW7CvcThWm/pdWalu3cg==", - "type": "package", - "path": "dawn.guard/1.9.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dawn.guard.1.9.0.nupkg.sha512", - "dawn.guard.nuspec", - "lib/netstandard1.0/Dawn.Guard.dll", - "lib/netstandard1.0/Dawn.Guard.xml", - "lib/netstandard2.0/Dawn.Guard.dll", - "lib/netstandard2.0/Dawn.Guard.xml" - ] - }, - "DnsClient/1.2.0": { - "sha512": "P34wUkeqU4FoQiOMV8OjpdeDZKs4d3r+VlHuKJ6eO5feiZgna3+9MF5orHRUn3DAv1g/HPE5hlkGucmxmsFfBw==", - "type": "package", - "path": "dnsclient/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dnsclient.1.2.0.nupkg.sha512", - "dnsclient.nuspec", - "lib/net45/DnsClient.dll", - "lib/net45/DnsClient.xml", - "lib/net471/DnsClient.dll", - "lib/net471/DnsClient.xml", - "lib/netstandard1.3/DnsClient.dll", - "lib/netstandard1.3/DnsClient.xml", - "lib/netstandard2.0/DnsClient.dll", - "lib/netstandard2.0/DnsClient.xml" - ] - }, - "DotNetty.Buffers/0.6.0": { - "sha512": "MjxoFMdKsSJ5CLKTFHTKYoFUZUvC6VHGx71vPLSflmR/X4dSm57/hVxhsQY3YU6aASeRjqgNiCHn4II+UNOIWQ==", - "type": "package", - "path": "dotnetty.buffers/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.buffers.0.6.0.nupkg.sha512", - "dotnetty.buffers.nuspec", - "lib/net45/DotNetty.Buffers.dll", - "lib/net45/DotNetty.Buffers.xml", - "lib/netstandard1.3/DotNetty.Buffers.dll", - "lib/netstandard1.3/DotNetty.Buffers.xml" - ] - }, - "DotNetty.Codecs/0.6.0": { - "sha512": "gLAVaII7A4er3ZwUJIAZpynDLsxNr7Acw61OoCDfzIAkcB2L6OJ9jPnKy8YtrtTnJXm0lNod5+7aRJTmaBbT0Q==", - "type": "package", - "path": "dotnetty.codecs/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.codecs.0.6.0.nupkg.sha512", - "dotnetty.codecs.nuspec", - "lib/net45/DotNetty.Codecs.dll", - "lib/net45/DotNetty.Codecs.xml", - "lib/netstandard1.3/DotNetty.Codecs.dll", - "lib/netstandard1.3/DotNetty.Codecs.xml" - ] - }, - "DotNetty.Codecs.Protobuf/0.6.0": { - "sha512": "j0feAIaVVlvWwWg0t1q8s1hjccS8ekTcS9gHyO2oEcuzwPKRynKyA/Kn7Lw5GqfMF0NgMHc3GdvesIh64R7KOQ==", - "type": "package", - "path": "dotnetty.codecs.protobuf/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.codecs.protobuf.0.6.0.nupkg.sha512", - "dotnetty.codecs.protobuf.nuspec", - "lib/net45/DotNetty.Codecs.Protobuf.dll", - "lib/net45/DotNetty.Codecs.Protobuf.xml", - "lib/netstandard1.3/DotNetty.Codecs.Protobuf.dll", - "lib/netstandard1.3/DotNetty.Codecs.Protobuf.xml" - ] - }, - "DotNetty.Common/0.6.0": { - "sha512": "aVD0bWoaH+Ou0wOTFzYEs3hrzsckUEkbdweVgwHsk7q0OTPgmPD/QkGxjNqjZ9diUyeGvHzYn7mFQGG5cgbYUA==", - "type": "package", - "path": "dotnetty.common/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.common.0.6.0.nupkg.sha512", - "dotnetty.common.nuspec", - "lib/net45/DotNetty.Common.dll", - "lib/net45/DotNetty.Common.xml", - "lib/netstandard1.3/DotNetty.Common.dll", - "lib/netstandard1.3/DotNetty.Common.xml" - ] - }, - "DotNetty.Handlers/0.6.0": { - "sha512": "LXj49DSk/PzC5r7MkXKpYit4t5DsUNpEw1m899SoV2gGVs9WooYUVyjOhKwyqBUPie4QgkYc6tsgAHNJUmDhbA==", - "type": "package", - "path": "dotnetty.handlers/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.handlers.0.6.0.nupkg.sha512", - "dotnetty.handlers.nuspec", - "lib/net45/DotNetty.Handlers.dll", - "lib/net45/DotNetty.Handlers.xml", - "lib/netstandard1.3/DotNetty.Handlers.dll", - "lib/netstandard1.3/DotNetty.Handlers.xml" - ] - }, - "DotNetty.Transport/0.6.0": { - "sha512": "HLYcbZfLDIVPhtr+x3nv2vvxBxKONWXeP/B1LaXvPeSfdvlOtvoxB0tuv92SaOdqq9NUCjAP59Nn9xofRrt8mA==", - "type": "package", - "path": "dotnetty.transport/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.transport.0.6.0.nupkg.sha512", - "dotnetty.transport.nuspec", - "lib/net45/DotNetty.Transport.dll", - "lib/net45/DotNetty.Transport.xml", - "lib/netstandard1.3/DotNetty.Transport.dll", - "lib/netstandard1.3/DotNetty.Transport.xml" - ] - }, - "Extensions.Data.xxHash.core20/1.0.2.1": { - "sha512": "gunLYoUUFQRcw2yM4eROw5YG6jZsEsoIj6BdKhU2kISNLoQ0IekknFZ42NXYWAkjH9D4F5YlwUUH2XJIkgeUfw==", - "type": "package", - "path": "extensions.data.xxhash.core20/1.0.2.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "extensions.data.xxhash.core20.1.0.2.1.nupkg.sha512", - "extensions.data.xxhash.core20.nuspec", - "lib/netstandard2.0/Extensions.Data.xxHash.core20.dll" - ] - }, - "Google.Protobuf/3.9.1": { - "sha512": "qCs2Tn+hJSllwIjQ2HQ4uNYGLbXWr3D6jJPFlJkIu1pErk4eQB1qlUcjA7NxA4JwOJzQbkZL45EbPGT/ZgwtsQ==", - "type": "package", - "path": "google.protobuf/3.9.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "google.protobuf.3.9.1.nupkg.sha512", - "google.protobuf.nuspec", - "lib/net45/Google.Protobuf.dll", - "lib/net45/Google.Protobuf.pdb", - "lib/net45/Google.Protobuf.xml", - "lib/netstandard1.0/Google.Protobuf.dll", - "lib/netstandard1.0/Google.Protobuf.pdb", - "lib/netstandard1.0/Google.Protobuf.xml", - "lib/netstandard2.0/Google.Protobuf.dll", - "lib/netstandard2.0/Google.Protobuf.pdb", - "lib/netstandard2.0/Google.Protobuf.xml" - ] - }, - "Ipfs.Core/0.51.1": { - "sha512": "tYIqY2DbxB0sAc/AcjQaeqaalrAcSCgnAtCXLr8VtB7lMg+LBjBwZgpNrDU1+fPhRQTEYmBpUy1Yzv/5ao35fw==", - "type": "package", - "path": "ipfs.core/0.51.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ipfs.core.0.51.1.nupkg.sha512", - "ipfs.core.nuspec", - "lib/net45/Ipfs.Core.dll", - "lib/net45/Ipfs.Core.xml", - "lib/netstandard1.4/Ipfs.Core.dll", - "lib/netstandard1.4/Ipfs.Core.xml", - "lib/netstandard2.0/Ipfs.Core.dll", - "lib/netstandard2.0/Ipfs.Core.xml" - ] - }, - "Ipfs.Engine/0.9.1": { - "sha512": "bnkS/HG5qMPmhsdrUSOBO2ZvROI0pLsatIAYQuhAAMeqAo06ji4t4gc2VZoeDcgVqOJyjZzKbBLmi7Lkw69eSg==", - "type": "package", - "path": "ipfs.engine/0.9.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ipfs.engine.0.9.1.nupkg.sha512", - "ipfs.engine.nuspec", - "lib/net461/Ipfs.Engine.dll", - "lib/net461/Ipfs.Engine.xml", - "lib/netstandard2.0/Ipfs.Engine.dll", - "lib/netstandard2.0/Ipfs.Engine.xml" - ] - }, - "Ipfs.HttpGateway/0.4.0": { - "sha512": "6Jl1gP5qVmbQAqSc7JLuTMw4pa2k+PVqpMKTnuOpCNyQBi508kQ/IU1DgVHlUsUyhZLxGIn+BHCxHPKk5tD2Rg==", - "type": "package", - "path": "ipfs.httpgateway/0.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ipfs.httpgateway.0.4.0.nupkg.sha512", - "ipfs.httpgateway.nuspec", - "lib/netcoreapp2.2/Ipfs.HttpGateway.Views.dll", - "lib/netcoreapp2.2/Ipfs.HttpGateway.dll", - "lib/netcoreapp2.2/Ipfs.HttpGateway.runtimeconfig.json", - "lib/netcoreapp2.2/Ipfs.HttpGateway.xml" - ] - }, - "IPNetwork2/2.1.2": { - "sha512": "DZF2SbtyqukkLSzyu0KqcXUBdhDXQ/K3QWM27vlvdPo3W+iI81pgUCNTwyynNyVc5Q9AIn0znFVVhHGbhiofUg==", - "type": "package", - "path": "ipnetwork2/2.1.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ipnetwork2.2.1.2.nupkg.sha512", - "ipnetwork2.nuspec", - "lib/net40/System.Net.IPNetwork.dll", - "lib/net45/System.Net.IPNetwork.dll", - "lib/net46/System.Net.IPNetwork.dll", - "lib/netstandard1.3/System.Net.IPNetwork.dll" - ] - }, - "Makaretu.Dns/1.4.1": { - "sha512": "mSGwiWrYV5XZsOl/bxAZ8Np/xHYTH2PE66Skh3iwR/g5iXGN2aH+wmzAnQo5XddKiTaU+5aXQbPThMN1K83bkA==", - "type": "package", - "path": "makaretu.dns/1.4.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Makaretu.Dns.dll", - "lib/net45/Makaretu.Dns.xml", - "lib/net472/Makaretu.Dns.dll", - "lib/net472/Makaretu.Dns.xml", - "lib/netstandard1.4/Makaretu.Dns.dll", - "lib/netstandard1.4/Makaretu.Dns.xml", - "lib/netstandard2.0/Makaretu.Dns.dll", - "lib/netstandard2.0/Makaretu.Dns.xml", - "makaretu.dns.1.4.1.nupkg.sha512", - "makaretu.dns.nuspec" - ] - }, - "Makaretu.Dns.Multicast/0.18.0": { - "sha512": "r6t6JlyFAdVVbSVpOljlDlaG6OhDi7988bnET9+6e9GrUXvvsPOyRQZ9j6k+ZJW6ZXrFrKuJlm4pRCgTDUq/Zg==", - "type": "package", - "path": "makaretu.dns.multicast/0.18.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net461/Makaretu.Dns.Multicast.dll", - "lib/net461/Makaretu.Dns.Multicast.xml", - "lib/netstandard1.4/Makaretu.Dns.Multicast.dll", - "lib/netstandard1.4/Makaretu.Dns.Multicast.xml", - "lib/netstandard2.0/Makaretu.Dns.Multicast.dll", - "lib/netstandard2.0/Makaretu.Dns.Multicast.xml", - "makaretu.dns.multicast.0.18.0.nupkg.sha512", - "makaretu.dns.multicast.nuspec" - ] - }, - "Makaretu.Dns.Unicast/0.9.0": { - "sha512": "bXxQUL9GyWsk4dr4k7k4JD4YhZterw8vvUf4oCUfmNXKcInTDvWsbL13SfZ4hn9AxXmOYvfO88tEnmxFDG+/Mw==", - "type": "package", - "path": "makaretu.dns.unicast/0.9.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net46/Makaretu.Dns.Unicast.dll", - "lib/net46/Makaretu.Dns.Unicast.xml", - "lib/netstandard1.4/Makaretu.Dns.Unicast.dll", - "lib/netstandard1.4/Makaretu.Dns.Unicast.xml", - "lib/netstandard2.0/Makaretu.Dns.Unicast.dll", - "lib/netstandard2.0/Makaretu.Dns.Unicast.xml", - "makaretu.dns.unicast.0.9.0.nupkg.sha512", - "makaretu.dns.unicast.nuspec" - ] - }, - "Makaretu.KBucket/0.5.0": { - "sha512": "SqCio1JHp3CAtfqNnsuhe4wQdPZDv2yAco0cW0uW9KfL3AC0clGsQCmzKAr2VvFaUkGMysB7HmWqzf6i0Zs3Fg==", - "type": "package", - "path": "makaretu.kbucket/0.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Makaretu.KBucket.dll", - "lib/net45/Makaretu.KBucket.xml", - "lib/netstandard1.4/Makaretu.KBucket.dll", - "lib/netstandard1.4/Makaretu.KBucket.xml", - "lib/netstandard2.0/Makaretu.KBucket.dll", - "lib/netstandard2.0/Makaretu.KBucket.xml", - "makaretu.kbucket.0.5.0.nupkg.sha512", - "makaretu.kbucket.nuspec" - ] - }, - "Microsoft.CSharp/4.0.1": { - "sha512": "wOdakWLyi6TEVyyPrE/UyMl9bTUFo3aDZGul9+X2rA2mnVIA0mRgVN6Q6OCPiToQ5RRZG2m4JzZAhCss+Qvd7A==", - "type": "package", - "path": "microsoft.csharp/4.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/Microsoft.CSharp.dll", - "lib/netstandard1.3/Microsoft.CSharp.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "microsoft.csharp.4.0.1.nupkg.sha512", - "microsoft.csharp.nuspec", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/Microsoft.CSharp.dll", - "ref/netcore50/Microsoft.CSharp.xml", - "ref/netcore50/de/Microsoft.CSharp.xml", - "ref/netcore50/es/Microsoft.CSharp.xml", - "ref/netcore50/fr/Microsoft.CSharp.xml", - "ref/netcore50/it/Microsoft.CSharp.xml", - "ref/netcore50/ja/Microsoft.CSharp.xml", - "ref/netcore50/ko/Microsoft.CSharp.xml", - "ref/netcore50/ru/Microsoft.CSharp.xml", - "ref/netcore50/zh-hans/Microsoft.CSharp.xml", - "ref/netcore50/zh-hant/Microsoft.CSharp.xml", - "ref/netstandard1.0/Microsoft.CSharp.dll", - "ref/netstandard1.0/Microsoft.CSharp.xml", - "ref/netstandard1.0/de/Microsoft.CSharp.xml", - "ref/netstandard1.0/es/Microsoft.CSharp.xml", - "ref/netstandard1.0/fr/Microsoft.CSharp.xml", - "ref/netstandard1.0/it/Microsoft.CSharp.xml", - "ref/netstandard1.0/ja/Microsoft.CSharp.xml", - "ref/netstandard1.0/ko/Microsoft.CSharp.xml", - "ref/netstandard1.0/ru/Microsoft.CSharp.xml", - "ref/netstandard1.0/zh-hans/Microsoft.CSharp.xml", - "ref/netstandard1.0/zh-hant/Microsoft.CSharp.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._" - ] - }, - "Microsoft.DotNet.PlatformAbstractions/2.0.4": { - "sha512": "2HjSGp63VCLQaeGadrLYR868g25mJHr+TFF81yWCaClzjUbU2vNDx6km7SUgPnoLVksE/1e7in88eh+oPtc4aQ==", - "type": "package", - "path": "microsoft.dotnet.platformabstractions/2.0.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net45/Microsoft.DotNet.PlatformAbstractions.dll", - "lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll", - "microsoft.dotnet.platformabstractions.2.0.4.nupkg.sha512", - "microsoft.dotnet.platformabstractions.nuspec" - ] - }, - "Microsoft.Extensions.Caching.Abstractions/2.1.2": { - "sha512": "3d5xYNZhqosir31mohbApOE1Iuci/WOfWit+/Cj6ZxS5SR28SbgskgP8M+ZmBZ5Gy7l1DT7fYikgqpC2tQE/nA==", - "type": "package", - "path": "microsoft.extensions.caching.abstractions/2.1.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.xml", - "microsoft.extensions.caching.abstractions.2.1.2.nupkg.sha512", - "microsoft.extensions.caching.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.Caching.Memory/2.1.2": { - "sha512": "97bIvx+m0XZYdXHWZgOa+KDzzaa8y/eq8fBqBogFGzKdN1+g4P1izA/Ar724G5Oc5t0kvLq2iZR64Tz1UL+TLg==", - "type": "package", - "path": "microsoft.extensions.caching.memory/2.1.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll", - "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.xml", - "microsoft.extensions.caching.memory.2.1.2.nupkg.sha512", - "microsoft.extensions.caching.memory.nuspec" - ] - }, - "Microsoft.Extensions.Configuration/2.1.1": { - "sha512": "LjVKO6P2y52c5ZhTLX/w8zc5H4Y3J/LJsgqTBj49TtFq/hAtVNue/WA0F6/7GMY90xhD7K0MDZ4qpOeWXbLvzg==", - "type": "package", - "path": "microsoft.extensions.configuration/2.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.xml", - "microsoft.extensions.configuration.2.1.1.nupkg.sha512", - "microsoft.extensions.configuration.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.Abstractions/2.2.0": { - "sha512": "M6u4cMWXxPtqyJJ03oezyqTORmSgTPpa2gZRKkEGCXXHhyGnM1cHjPTzq5sevYS1VJEMb2TZj60mAc+hDoPPcQ==", - "type": "package", - "path": "microsoft.extensions.configuration.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.xml", - "microsoft.extensions.configuration.abstractions.2.2.0.nupkg.sha512", - "microsoft.extensions.configuration.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.Binder/2.0.0": { - "sha512": "IznHHzGUtrdpuQqIUdmzF6TYPcsYHONhHh3o9dGp39sX/9Zfmt476UnhvU0UhXgJnXXAikt/MpN6AuSLCCMdEQ==", - "type": "package", - "path": "microsoft.extensions.configuration.binder/2.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.xml", - "microsoft.extensions.configuration.binder.2.0.0.nupkg.sha512", - "microsoft.extensions.configuration.binder.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.FileExtensions/2.1.1": { - "sha512": "CDk5CwG0YzlRgvl65J0iK6ahrX12yMRrEat3yVTXjWC+GN9Jg9zHZu2IE4cQIPAMA/IiAI5KjgL08fhP3fPCkw==", - "type": "package", - "path": "microsoft.extensions.configuration.fileextensions/2.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.xml", - "microsoft.extensions.configuration.fileextensions.2.1.1.nupkg.sha512", - "microsoft.extensions.configuration.fileextensions.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.Json/2.1.1": { - "sha512": "IFpONpvdhVEE3S3F4fTYkpT/GyIHtumy2m0HniQanJ80Pj/pUF3Z4wjrHEp1G78rPD+WTo5fRlhdJfuU1Tv2GQ==", - "type": "package", - "path": "microsoft.extensions.configuration.json/2.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.xml", - "microsoft.extensions.configuration.json.2.1.1.nupkg.sha512", - "microsoft.extensions.configuration.json.nuspec" - ] - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/2.1.1": { - "sha512": "MgYpU5cwZohUMKKg3sbPhvGG+eAZ/59E9UwPwlrUkyXU+PGzqwZg9yyQNjhxuAWmoNoFReoemeCku50prYSGzA==", - "type": "package", - "path": "microsoft.extensions.dependencyinjection.abstractions/2.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml", - "microsoft.extensions.dependencyinjection.abstractions.2.1.1.nupkg.sha512", - "microsoft.extensions.dependencyinjection.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.DependencyModel/2.0.4": { - "sha512": "jnHAeijsfJFQXdXmnYK/NhQIkgBUeth//RZZkf0ldIKC+jARbf7YxbA9uTrs/EPhuQxHXaDxVuMyscgmL+UqfA==", - "type": "package", - "path": "microsoft.extensions.dependencymodel/2.0.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net451/Microsoft.Extensions.DependencyModel.dll", - "lib/netstandard1.3/Microsoft.Extensions.DependencyModel.dll", - "lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll", - "microsoft.extensions.dependencymodel.2.0.4.nupkg.sha512", - "microsoft.extensions.dependencymodel.nuspec" - ] - }, - "Microsoft.Extensions.FileProviders.Abstractions/2.1.1": { - "sha512": "UEQB5/QPuLYaCvScZQ9llhcks5xyEUKh41D615FoehRAF9UgGVmXHcCSOH8idHHLRoKm+OJJjEy1oywvuaL33w==", - "type": "package", - "path": "microsoft.extensions.fileproviders.abstractions/2.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.xml", - "microsoft.extensions.fileproviders.abstractions.2.1.1.nupkg.sha512", - "microsoft.extensions.fileproviders.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.FileProviders.Physical/2.1.1": { - "sha512": "kVCvLm1ePchUgRrQZrno07Mn6knDAzR7vl6eRaI/fem0u6ODg+RTwOYLs4XL39Ttuu+BzEwqzHu3DtDgXT8+vQ==", - "type": "package", - "path": "microsoft.extensions.fileproviders.physical/2.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.xml", - "microsoft.extensions.fileproviders.physical.2.1.1.nupkg.sha512", - "microsoft.extensions.fileproviders.physical.nuspec" - ] - }, - "Microsoft.Extensions.FileSystemGlobbing/2.1.1": { - "sha512": "4QDzyCN8cJnThY6mK9SnzovyCZ8KCG9jmC9KqHfFGtazJvmNZP1gcyBkPmqMjP0qwbmEUUyqyA9LLn3FrYXTGw==", - "type": "package", - "path": "microsoft.extensions.filesystemglobbing/2.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll", - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.xml", - "microsoft.extensions.filesystemglobbing.2.1.1.nupkg.sha512", - "microsoft.extensions.filesystemglobbing.nuspec" - ] - }, - "Microsoft.Extensions.Logging/2.0.0": { - "sha512": "VP10syWV/vxYYMKgZ2eDESmUsz3gPxvBn5J6tkVN8lI4M+dF43RN8fWsEPbcAneDmZrHl3Pv23z05nmyGkJlpg==", - "type": "package", - "path": "microsoft.extensions.logging/2.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Logging.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.xml", - "microsoft.extensions.logging.2.0.0.nupkg.sha512", - "microsoft.extensions.logging.nuspec" - ] - }, - "Microsoft.Extensions.Logging.Abstractions/2.0.0": { - "sha512": "6ZCllUYGFukkymSTx3Yr0G/ajRxoNJp7/FqSxSB4fGISST54ifBhgu4Nc0ItGi3i6DqwuNd8SUyObmiC++AO2Q==", - "type": "package", - "path": "microsoft.extensions.logging.abstractions/2.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.xml", - "microsoft.extensions.logging.abstractions.2.0.0.nupkg.sha512", - "microsoft.extensions.logging.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.Options/2.1.1": { - "sha512": "V7lXCU78lAbzaulCGFKojcCyG8RTJicEbiBkPJjFqiqXwndEBBIehdXRMWEVU3UtzQ1yDvphiWUL9th6/4gJ7w==", - "type": "package", - "path": "microsoft.extensions.options/2.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Options.dll", - "lib/netstandard2.0/Microsoft.Extensions.Options.xml", - "microsoft.extensions.options.2.1.1.nupkg.sha512", - "microsoft.extensions.options.nuspec" - ] - }, - "Microsoft.Extensions.Options.ConfigurationExtensions/2.0.0": { - "sha512": "Y/lGICwO27fCkQRK3tZseVzFjZaxfGmui990E67sB4MuiPzdJHnJDS/BeYWrHShSSBgCl4KyKRx4ux686fftPg==", - "type": "package", - "path": "microsoft.extensions.options.configurationextensions/2.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.xml", - "microsoft.extensions.options.configurationextensions.2.0.0.nupkg.sha512", - "microsoft.extensions.options.configurationextensions.nuspec" - ] - }, - "Microsoft.Extensions.Primitives/2.2.0": { - "sha512": "vpH+o7f8obVx65PiEtBXxTwL5RosK60fNIMy/y8WGE4/r4IvAqQGukOzMhxsp//Accvd7kmukyQTVkqVYdaDyA==", - "type": "package", - "path": "microsoft.extensions.primitives/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll", - "lib/netstandard2.0/Microsoft.Extensions.Primitives.xml", - "microsoft.extensions.primitives.2.2.0.nupkg.sha512", - "microsoft.extensions.primitives.nuspec" - ] - }, - "Microsoft.IO.RecyclableMemoryStream/1.2.2": { - "sha512": "rVilnCM72Wi9oCAh6sMmOCDSy71/UskHLxhk+OXLJp4C6Uby2GOUeL/9fHMaAYCL4iJECXOOFEfhfgYE5u8HKw==", - "type": "package", - "path": "microsoft.io.recyclablememorystream/1.2.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net40/Microsoft.IO.RecyclableMemoryStream.dll", - "lib/net40/Microsoft.IO.RecyclableMemoryStream.pdb", - "lib/net45/Microsoft.IO.RecyclableMemoryStream.dll", - "lib/net45/Microsoft.IO.RecyclableMemoryStream.pdb", - "lib/netstandard1.4/Microsoft.IO.RecyclableMemoryStream.deps.json", - "lib/netstandard1.4/Microsoft.IO.RecyclableMemoryStream.dll", - "lib/netstandard1.4/Microsoft.IO.RecyclableMemoryStream.pdb", - "microsoft.io.recyclablememorystream.1.2.2.nupkg.sha512", - "microsoft.io.recyclablememorystream.nuspec" - ] - }, - "Microsoft.NETCore.Platforms/2.1.0": { - "sha512": "ok+RPAtESz/9MUXeIEz6Lv5XAGQsaNmEYXMsgVALj4D7kqC8gveKWXWXbufLySR2fWrwZf8smyN5RmHu0e4BHA==", - "type": "package", - "path": "microsoft.netcore.platforms/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/_._", - "microsoft.netcore.platforms.2.1.0.nupkg.sha512", - "microsoft.netcore.platforms.nuspec", - "runtime.json", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "Microsoft.NETCore.Targets/1.1.0": { - "sha512": "HvAzpoaIqrmZfsHAN4rLFji0r7YY5TP8r3SdkziXN7qU9KCpRIG+zfcBx+mIri/jkBNqjq4iWIfuYNtFBjSQ/g==", - "type": "package", - "path": "microsoft.netcore.targets/1.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "microsoft.netcore.targets.1.1.0.nupkg.sha512", - "microsoft.netcore.targets.nuspec", - "runtime.json" - ] - }, - "Microsoft.Win32.Primitives/4.3.0": { - "sha512": "Nm8Hp51y9tYcK3xD6qk43Wjftrg1mdH24CCJsTb6gr7HS21U1uA+CKPGEtUcVZbjU1y8Kynzm5eoJ7Pnx5gm8A==", - "type": "package", - "path": "microsoft.win32.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/Microsoft.Win32.Primitives.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "microsoft.win32.primitives.4.3.0.nupkg.sha512", - "microsoft.win32.primitives.nuspec", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/Microsoft.Win32.Primitives.dll", - "ref/netstandard1.3/Microsoft.Win32.Primitives.dll", - "ref/netstandard1.3/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/de/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/es/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/fr/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/it/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/ja/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/ko/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/ru/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/zh-hans/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/zh-hant/Microsoft.Win32.Primitives.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._" - ] - }, - "Microsoft.Win32.Registry/4.0.0": { - "sha512": "q+eLtROUAQ3OxYA5mpQrgyFgzLQxIyrfT2eLpYX5IEPlHmIio2nh4F5bgOaQoGOV865kFKZZso9Oq9RlazvXtg==", - "type": "package", - "path": "microsoft.win32.registry/4.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/net46/Microsoft.Win32.Registry.dll", - "microsoft.win32.registry.4.0.0.nupkg.sha512", - "microsoft.win32.registry.nuspec", - "ref/net46/Microsoft.Win32.Registry.dll", - "ref/netstandard1.3/Microsoft.Win32.Registry.dll", - "ref/netstandard1.3/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/de/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/es/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/fr/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/it/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/ja/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/ko/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/ru/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/zh-hans/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/zh-hant/Microsoft.Win32.Registry.xml", - "runtimes/unix/lib/netstandard1.3/Microsoft.Win32.Registry.dll", - "runtimes/win/lib/net46/Microsoft.Win32.Registry.dll", - "runtimes/win/lib/netcore50/_._", - "runtimes/win/lib/netstandard1.3/Microsoft.Win32.Registry.dll" - ] - }, - "MongoDB.Bson/2.7.0": { - "sha512": "BBPKyeYmhRduXuDQsFvB2bw34rAqJrT9UZPHAVLGLtvp5wYxhzP8r7SrSn//f91Xst09Ww/5lq3DD5byn0fdLw==", - "type": "package", - "path": "mongodb.bson/2.7.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "License.rtf", - "lib/net45/MongoDB.Bson.dll", - "lib/net45/MongoDB.Bson.xml", - "lib/netstandard1.5/MongoDB.Bson.dll", - "lib/netstandard1.5/MongoDB.Bson.xml", - "mongodb.bson.2.7.0.nupkg.sha512", - "mongodb.bson.nuspec" - ] - }, - "Multiformats.Base/2.0.1": { - "sha512": "JherI2cl97crsQHN5pwwNIlz004D64szvvXRRq8XVXQR2ZOFTaW5UEs8sJmt80bhW3cHH7XP4ooCqGYr/WBNRw==", - "type": "package", - "path": "multiformats.base/2.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net461/Multiformats.Base.dll", - "lib/netstandard1.6/Multiformats.Base.dll", - "multiformats.base.2.0.1.nupkg.sha512", - "multiformats.base.nuspec" - ] - }, - "Multiformats.Hash/1.5.0": { - "sha512": "f9HstrBNHUWs0WFhYH7H4H3VatzTVop+XWp0QDFW7f9JzeIj2fnz21P0IrgwR8H6wl1ujAEh+5yf30XlqRDcaQ==", - "type": "package", - "path": "multiformats.hash/1.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net461/Multiformats.Hash.dll", - "lib/net461/Multiformats.Hash.pdb", - "lib/netstandard1.6/Multiformats.Hash.dll", - "lib/netstandard1.6/Multiformats.Hash.pdb", - "lib/netstandard2.0/Multiformats.Hash.dll", - "lib/netstandard2.0/Multiformats.Hash.pdb", - "multiformats.hash.1.5.0.nupkg.sha512", - "multiformats.hash.nuspec" - ] - }, - "murmurhash/1.0.2": { - "sha512": "Yw9+sYL3qdTEXDKAEeiXsVwsP2K2nyWOxgvbDD1w5j+yu0CYk5edLvGmmJHqqFxuBFrVsgb7iF2XGprRlt+SEA==", - "type": "package", - "path": "murmurhash/1.0.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net35/MurmurHash.dll", - "lib/net40/MurmurHash.dll", - "lib/net45/MurmurHash.dll", - "lib/netstandard1.4/MurmurHash.dll", - "murmurhash.1.0.2.nupkg.sha512", - "murmurhash.nuspec" - ] - }, - "Nethereum.Hex/3.3.0": { - "sha512": "N5RP4U901vUvRj74DMAwFm1gxXrrfVgKcObtcPczvY2yczP7gIhMCuYQWIzBzau43UwgUAn1ZjvgsWZjWxdotg==", - "type": "package", - "path": "nethereum.hex/3.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.md", - "lib/net451/Nethereum.Hex.dll", - "lib/netcoreapp2.1/Nethereum.Hex.dll", - "lib/netstandard1.1/Nethereum.Hex.dll", - "lib/netstandard2.0/Nethereum.Hex.dll", - "nethereum.hex.3.3.0.nupkg.sha512", - "nethereum.hex.nuspec" - ] - }, - "Nethereum.RLP/3.3.0": { - "sha512": "hWRhRMvpkkXiuCO5Jo9n+DBV03xRU06KTOh+ZBavC/jdLCnuPdwZbpciZt9BMMtg3z+Bl6UmFGOZxDuZPNi2zg==", - "type": "package", - "path": "nethereum.rlp/3.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.md", - "lib/net451/Nethereum.RLP.dll", - "lib/netcoreapp2.1/Nethereum.RLP.dll", - "lib/netstandard1.1/Nethereum.RLP.dll", - "lib/netstandard2.0/Nethereum.RLP.dll", - "nethereum.rlp.3.3.0.nupkg.sha512", - "nethereum.rlp.nuspec" - ] - }, - "NETStandard.Library/1.6.1": { - "sha512": "CXLcLbtJJeiW9ivOLlnU5IY5Mg7jitMBbc1IX71pNqDtCAc61e7yphLf8F38OQ85MP/5552HoGBw7rgSgnfL0A==", - "type": "package", - "path": "netstandard.library/1.6.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "netstandard.library.1.6.1.nupkg.sha512", - "netstandard.library.nuspec" - ] - }, - "Newtonsoft.Json/12.0.2": { - "sha512": "rTK0s2EKlfHsQsH6Yx2smvcTCeyoDNgCW7FEYyV01drPlh2T243PR2DiDXqtC5N4GDm4Ma/lkxfW5a/4793vbA==", - "type": "package", - "path": "newtonsoft.json/12.0.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.md", - "lib/net20/Newtonsoft.Json.dll", - "lib/net20/Newtonsoft.Json.xml", - "lib/net35/Newtonsoft.Json.dll", - "lib/net35/Newtonsoft.Json.xml", - "lib/net40/Newtonsoft.Json.dll", - "lib/net40/Newtonsoft.Json.xml", - "lib/net45/Newtonsoft.Json.dll", - "lib/net45/Newtonsoft.Json.xml", - "lib/netstandard1.0/Newtonsoft.Json.dll", - "lib/netstandard1.0/Newtonsoft.Json.xml", - "lib/netstandard1.3/Newtonsoft.Json.dll", - "lib/netstandard1.3/Newtonsoft.Json.xml", - "lib/netstandard2.0/Newtonsoft.Json.dll", - "lib/netstandard2.0/Newtonsoft.Json.xml", - "lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.dll", - "lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.xml", - "lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.dll", - "lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.xml", - "newtonsoft.json.12.0.2.nupkg.sha512", - "newtonsoft.json.nuspec" - ] - }, - "Nito.AsyncEx/5.0.0": { - "sha512": "EgeMl8BoaLg6mOXs7MVjl3uqUqZlL0uYti2/G128EA60U9UqoYlF0AMtGOA1/EP+bvrjcvixK8D3MLpwr2dnSw==", - "type": "package", - "path": "nito.asyncex/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "nito.asyncex.5.0.0.nupkg.sha512", - "nito.asyncex.nuspec" - ] - }, - "Nito.AsyncEx.Context/5.0.0": { - "sha512": "Qnth1Ye+QSLg8P3fSFYzk7ue6oUUHQcKpLitgAig8xRFqTK5W1KTlfxF/Z8Eo0BuqZ17a5fAGtXrdKJsLqivZw==", - "type": "package", - "path": "nito.asyncex.context/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Nito.AsyncEx.Context.dll", - "lib/netstandard1.3/Nito.AsyncEx.Context.xml", - "lib/netstandard2.0/Nito.AsyncEx.Context.dll", - "lib/netstandard2.0/Nito.AsyncEx.Context.xml", - "nito.asyncex.context.5.0.0.nupkg.sha512", - "nito.asyncex.context.nuspec" - ] - }, - "Nito.AsyncEx.Coordination/5.0.0": { - "sha512": "kjauyO8UMo/FGZO/M8TdjXB8ZlBPFOiRN8yakThaGQbYOywazQ0kGZ39SNr2gNNzsTxbZOUudBMYNo+IrtscbA==", - "type": "package", - "path": "nito.asyncex.coordination/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Nito.AsyncEx.Coordination.dll", - "lib/netstandard1.3/Nito.AsyncEx.Coordination.xml", - "lib/netstandard2.0/Nito.AsyncEx.Coordination.dll", - "lib/netstandard2.0/Nito.AsyncEx.Coordination.xml", - "nito.asyncex.coordination.5.0.0.nupkg.sha512", - "nito.asyncex.coordination.nuspec" - ] - }, - "Nito.AsyncEx.Interop.WaitHandles/5.0.0": { - "sha512": "roX7jQ7AAEw3CUq/oRj/ACcN1XtfO6fSbYSAjMtjvacN9pqY7Uq2reBFaosbMaMt16EcW+Xw3PwczKzHdZFPRA==", - "type": "package", - "path": "nito.asyncex.interop.waithandles/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Nito.AsyncEx.Interop.WaitHandles.dll", - "lib/netstandard1.3/Nito.AsyncEx.Interop.WaitHandles.xml", - "lib/netstandard2.0/Nito.AsyncEx.Interop.WaitHandles.dll", - "lib/netstandard2.0/Nito.AsyncEx.Interop.WaitHandles.xml", - "nito.asyncex.interop.waithandles.5.0.0.nupkg.sha512", - "nito.asyncex.interop.waithandles.nuspec" - ] - }, - "Nito.AsyncEx.Oop/5.0.0": { - "sha512": "SKyHsxzXD8b69tPPgMDh7mgMx7GdaSfJ1h2eNS+z2Z/vHsutXTl1sYLOO30fiIkv1umvI1g8GySvYLpVlpt6yA==", - "type": "package", - "path": "nito.asyncex.oop/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Nito.AsyncEx.Oop.dll", - "lib/netstandard1.3/Nito.AsyncEx.Oop.xml", - "lib/netstandard2.0/Nito.AsyncEx.Oop.dll", - "lib/netstandard2.0/Nito.AsyncEx.Oop.xml", - "nito.asyncex.oop.5.0.0.nupkg.sha512", - "nito.asyncex.oop.nuspec" - ] - }, - "Nito.AsyncEx.Tasks/5.0.0": { - "sha512": "ZtvotignafOLteP4oEjVcF3k2L8h73QUCaFpVKWbU+EOlW/I+JGkpMoXIl0rlwPcDmR84RxzggLRUNMaWlOosA==", - "type": "package", - "path": "nito.asyncex.tasks/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Nito.AsyncEx.Tasks.dll", - "lib/netstandard1.3/Nito.AsyncEx.Tasks.xml", - "lib/netstandard2.0/Nito.AsyncEx.Tasks.dll", - "lib/netstandard2.0/Nito.AsyncEx.Tasks.xml", - "nito.asyncex.tasks.5.0.0.nupkg.sha512", - "nito.asyncex.tasks.nuspec" - ] - }, - "Nito.Cancellation/1.0.5": { - "sha512": "/MehN8HvuOgPZsNmqyu0UdPWXCAJVqcy0LEqw1Ch1++ag1h0qPt5YLH+BCIMFDC5UMW/4RzJW2GdRDYg7Vi2yA==", - "type": "package", - "path": "nito.cancellation/1.0.5", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.0/Nito.Cancellation.dll", - "lib/netstandard1.0/Nito.Cancellation.xml", - "lib/netstandard2.0/Nito.Cancellation.dll", - "lib/netstandard2.0/Nito.Cancellation.xml", - "nito.cancellation.1.0.5.nupkg.sha512", - "nito.cancellation.nuspec" - ] - }, - "Nito.Collections.Deque/1.0.4": { - "sha512": "yGDKqCQ61i97MyfEUYG6+ln5vxpx11uA5M9+VV9B7stticbFm19YMI/G9w4AFYVBj5PbPi138P8IovkMFAL0Aw==", - "type": "package", - "path": "nito.collections.deque/1.0.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.0/Nito.Collections.Deque.dll", - "lib/netstandard1.0/Nito.Collections.Deque.xml", - "lib/netstandard2.0/Nito.Collections.Deque.dll", - "lib/netstandard2.0/Nito.Collections.Deque.xml", - "nito.collections.deque.1.0.4.nupkg.sha512", - "nito.collections.deque.nuspec" - ] - }, - "Nito.Disposables/2.0.0": { - "sha512": "ExJl/jTjegSLHGcwnmaYaI5xIlrefAsVdeLft7VLtXI2+W5irihiu36LizWvlaUpzY1/llo+YSh09uSHMu2VFw==", - "type": "package", - "path": "nito.disposables/2.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.0/Nito.Disposables.dll", - "lib/netstandard1.0/Nito.Disposables.pdb", - "lib/netstandard1.0/Nito.Disposables.xml", - "lib/netstandard2.0/Nito.Disposables.dll", - "lib/netstandard2.0/Nito.Disposables.pdb", - "lib/netstandard2.0/Nito.Disposables.xml", - "nito.disposables.2.0.0.nupkg.sha512", - "nito.disposables.nuspec" - ] - }, - "NLog/4.6.5": { - "sha512": "iM0VsNBxnyzyoJWUBrXkFJvHRWTeaVAPjRETzknnGNVicD7C4FYpWszzdF43ugTh6a+StbZffaII9KKEFM5xsA==", - "type": "package", - "path": "nlog/4.6.5", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/monoandroid44/NLog.dll", - "lib/monoandroid44/NLog.xml", - "lib/net35/NLog.dll", - "lib/net35/NLog.xml", - "lib/net40-client/NLog.dll", - "lib/net40-client/NLog.xml", - "lib/net45/NLog.dll", - "lib/net45/NLog.xml", - "lib/netstandard1.3/NLog.dll", - "lib/netstandard1.3/NLog.xml", - "lib/netstandard1.5/NLog.dll", - "lib/netstandard1.5/NLog.xml", - "lib/netstandard2.0/NLog.dll", - "lib/netstandard2.0/NLog.xml", - "lib/sl4/NLog.dll", - "lib/sl4/NLog.xml", - "lib/sl5/NLog.dll", - "lib/sl5/NLog.xml", - "lib/wp8/NLog.dll", - "lib/wp8/NLog.xml", - "lib/xamarinios10/NLog.dll", - "lib/xamarinios10/NLog.xml", - "nlog.4.6.5.nupkg.sha512", - "nlog.nuspec" - ] - }, - "NLog.StructuredLogging.Json/4.0.0": { - "sha512": "REL10hQplUeZozMAeYSbljkMT0PrCMO0G0ILnuWlleqTkl52CD99ILWUq+WyJiPQoxRlzDVYUZkMQQx65ez32g==", - "type": "package", - "path": "nlog.structuredlogging.json/4.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/NLog.StructuredLogging.Json.dll", - "lib/net45/NLog.StructuredLogging.Json.pdb", - "lib/net45/NLog.StructuredLogging.Json.runtimeconfig.json", - "lib/netstandard2.0/NLog.StructuredLogging.Json.dll", - "lib/netstandard2.0/NLog.StructuredLogging.Json.pdb", - "lib/netstandard2.0/NLog.StructuredLogging.Json.runtimeconfig.json", - "nlog.structuredlogging.json.4.0.0.nupkg.sha512", - "nlog.structuredlogging.json.nuspec" - ] - }, - "NLog.Targets.Seq/1.1.0": { - "sha512": "L1M9oX0gWbp0aqZWMRkm7qnGmcvMt4KYonwtnvNfQQ0+dN6gxXufvR3dEOWNMtyofPQFUhz8VzcJ9y1USkXIqg==", - "type": "package", - "path": "nlog.targets.seq/1.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/NLog.Targets.Seq.dll", - "lib/net45/NLog.Targets.Seq.xml", - "lib/netstandard2.0/NLog.Targets.Seq.dll", - "lib/netstandard2.0/NLog.Targets.Seq.xml", - "nlog.targets.seq.1.1.0.nupkg.sha512", - "nlog.targets.seq.nuspec" - ] - }, - "NLog.Targets.Syslog/5.1.0": { - "sha512": "W97RMEDe3dO98rIhX0pd3fdpTu7vsc91uxGi10W3zPAxdD78GUqtGeHyqmjsPXT0B8vtiYPGD26QXao/Jqo5YA==", - "type": "package", - "path": "nlog.targets.syslog/5.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net452/NLog.Targets.Syslog.dll", - "lib/net452/NLog.Targets.Syslog.xml", - "lib/netstandard2.0/NLog.Targets.Syslog.dll", - "lib/netstandard2.0/NLog.Targets.Syslog.xml", - "nlog.targets.syslog.5.1.0.nupkg.sha512", - "nlog.targets.syslog.nuspec" - ] - }, - "PeerTalk/0.11.3": { - "sha512": "FehIZRZZgu3bTz3uQfTTRfKnLi0pZpHu31yPFU4mXzc7jdBxYq5LUiMM/NE8dHwkV00XyAa+dkFqrPGD5ui0Cg==", - "type": "package", - "path": "peertalk/0.11.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net461/PeerTalk.dll", - "lib/net461/PeerTalk.xml", - "lib/netstandard1.4/PeerTalk.dll", - "lib/netstandard1.4/PeerTalk.xml", - "lib/netstandard2.0/PeerTalk.dll", - "lib/netstandard2.0/PeerTalk.xml", - "peertalk.0.11.3.nupkg.sha512", - "peertalk.nuspec" - ] - }, - "PeterO.Cbor/3.1.0": { - "sha512": "J5xQ+7RJAdO9plzuFqkq92rlMXiUkzY9H8FvpuNw/QN2VNChHgoAjw9EhkBeiqJa6HvCmNy/sf73afDNsMXvXQ==", - "type": "package", - "path": "petero.cbor/3.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.0/CBOR.dll", - "lib/netstandard1.0/CBOR.xml", - "petero.cbor.3.1.0.nupkg.sha512", - "petero.cbor.nuspec" - ] - }, - "PeterO.Numbers/1.0.2": { - "sha512": "cQTX4A/w7PQjEbkTRtIz24cj9bkHnULpG6BcI5zxIS7KwdxQPxJZepUx7BBJHP1hSd+yZ0hJYp+wpX7c55zFlg==", - "type": "package", - "path": "petero.numbers/1.0.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.0/Numbers.dll", - "lib/netstandard1.0/Numbers.xml", - "petero.numbers.1.0.2.nupkg.sha512", - "petero.numbers.nuspec" - ] - }, - "Polly/7.1.0": { - "sha512": "mpQsvlEn4ipgICh5CGyVLPV93RFVzOrBG7wPZfzY8gExh7XgWQn0GCDY9nudbUEJ12UiGPP9i4+CohghBvzhmg==", - "type": "package", - "path": "polly/7.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.1/Polly.dll", - "lib/netstandard1.1/Polly.xml", - "lib/netstandard2.0/Polly.dll", - "lib/netstandard2.0/Polly.xml", - "polly.7.1.0.nupkg.sha512", - "polly.nuspec" - ] - }, - "Portable.BouncyCastle/1.8.5": { - "sha512": "EaCgmntbH1sOzemRTqyXSqYjB6pLH7VCYHhhDYZ59guHSD5qPwhIYa7kfy0QUlmTRt9IXhaXdFhNuBUArp70Ng==", - "type": "package", - "path": "portable.bouncycastle/1.8.5", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net40/BouncyCastle.Crypto.dll", - "lib/net40/BouncyCastle.Crypto.xml", - "lib/netstandard1.0/BouncyCastle.Crypto.dll", - "lib/netstandard1.0/BouncyCastle.Crypto.xml", - "lib/netstandard1.3/BouncyCastle.Crypto.dll", - "lib/netstandard1.3/BouncyCastle.Crypto.xml", - "lib/netstandard2.0/BouncyCastle.Crypto.dll", - "lib/netstandard2.0/BouncyCastle.Crypto.xml", - "portable.bouncycastle.1.8.5.nupkg.sha512", - "portable.bouncycastle.nuspec" - ] - }, - "protobuf-net/2.4.0": { - "sha512": "j37MD1p1s9NdX8P5+IaY2J9p2382xiL1VP3mxYu0g+G/kf2YM2grFa1jJPO+0WDJNl1XhNPO0Q5yBEcbX77hBQ==", - "type": "package", - "path": "protobuf-net/2.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net20/protobuf-net.dll", - "lib/net20/protobuf-net.xml", - "lib/net35/protobuf-net.dll", - "lib/net35/protobuf-net.xml", - "lib/net40/protobuf-net.dll", - "lib/net40/protobuf-net.xml", - "lib/netcoreapp2.1/protobuf-net.dll", - "lib/netcoreapp2.1/protobuf-net.xml", - "lib/netstandard1.0/protobuf-net.dll", - "lib/netstandard1.0/protobuf-net.xml", - "lib/netstandard1.3/protobuf-net.dll", - "lib/netstandard1.3/protobuf-net.xml", - "lib/netstandard2.0/protobuf-net.dll", - "lib/netstandard2.0/protobuf-net.xml", - "lib/uap10.0/protobuf-net.dll", - "lib/uap10.0/protobuf-net.pri", - "lib/uap10.0/protobuf-net.xml", - "protobuf-net.2.4.0.nupkg.sha512", - "protobuf-net.nuspec" - ] - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "7VSGO0URRKoMEAq0Sc9cRz8mb6zbyx/BZDEWhgPdzzpmFhkam3fJ1DAGWFXBI4nGlma+uPKpfuMQP5LXRnOH5g==", - "type": "package", - "path": "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/debian.8-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "0oAaTAm6e2oVH+/Zttt0cuhGaePQYKII1dY8iaqP7CvOpVKgLybKRFvQjXR2LtxXOXTVPNv14j0ot8uV+HrUmw==", - "type": "package", - "path": "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/fedora.23-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "G24ibsCNi5Kbz0oXWynBoRgtGvsw5ZSVEWjv13/KiCAM8C6wz9zzcCniMeQFIkJ2tasjo2kXlvlBZhplL51kGg==", - "type": "package", - "path": "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/fedora.24-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.native.System/4.3.0": { - "sha512": "KZxalv/9yvGXLj49HHJ4N9GKyeiMt5wJkU8S/x3nKA3/EMkjKkmhwdO6d4Wlz3byjJ3OQU8KKlZ2iN5/1TMdyA==", - "type": "package", - "path": "runtime.native.system/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.4.3.0.nupkg.sha512", - "runtime.native.system.nuspec" - ] - }, - "runtime.native.System.IO.Compression/4.3.0": { - "sha512": "v/HwyslDJwFLsHwevuBsIW5uSVGx3aoMinU6SgM4vmIf0V7GIVA0kNvKVKdYCavE9CBmmzMFKyjSTXJqx5yYkQ==", - "type": "package", - "path": "runtime.native.system.io.compression/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.io.compression.4.3.0.nupkg.sha512", - "runtime.native.system.io.compression.nuspec" - ] - }, - "runtime.native.System.Net.Http/4.3.0": { - "sha512": "3dHltnVFR398crWINmbeQOie+wg22R56NJ4vPUrAXOESXmrdPLCcOcvf56t8Xcj9rq9qwlrNkvbePYzi1tt5GA==", - "type": "package", - "path": "runtime.native.system.net.http/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.net.http.4.3.0.nupkg.sha512", - "runtime.native.system.net.http.nuspec" - ] - }, - "runtime.native.System.Net.Security/4.3.0": { - "sha512": "xdsIagJutS/0Mt/OHtKVvDkOBwH9YeeOxEMBTSxunHwtUoI294ONmqVak623IDZU2vv0uM9aJVeDR6cn0LEmLQ==", - "type": "package", - "path": "runtime.native.system.net.security/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.net.security.4.3.0.nupkg.sha512", - "runtime.native.system.net.security.nuspec" - ] - }, - "runtime.native.System.Security.Cryptography.Apple/4.3.1": { - "sha512": "UPrVPlqPRSVZaB4ADmbsQ77KXn9ORiWXyA1RP2W2+byCh3bhgT1bQz0jbeOoog9/2oTQ5wWZSDSMeb74MjezcA==", - "type": "package", - "path": "runtime.native.system.security.cryptography.apple/4.3.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.security.cryptography.apple.4.3.1.nupkg.sha512", - "runtime.native.system.security.cryptography.apple.nuspec" - ] - }, - "runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "QR1OwtwehHxSeQvZKXe+iSd+d3XZNkEcuWMFYa2i0aG1l+lR739HPicKMlTbJst3spmeekDVBUS7SeS26s4U/g==", - "type": "package", - "path": "runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.native.system.security.cryptography.openssl.nuspec" - ] - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "I+GNKGg2xCHueRd1m9PzeEW7WLbNNLznmTuEi8/vZX71HudUbx1UTwlGkiwMri7JLl8hGaIAWnA/GONhu+LOyQ==", - "type": "package", - "path": "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/opensuse.13.2-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "1Z3TAq1ytS1IBRtPXJvEUZdVsfWfeNEhBkbiOCGEl9wwAfsjP2lz3ZFDx5tq8p60/EqbS0HItG5piHuB71RjoA==", - "type": "package", - "path": "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/opensuse.42.1-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.1": { - "sha512": "t15yGf5r6vMV1rB5O6TgfXKChtCaN3niwFw44M2ImX3eZ8yzueplqMqXPCbWzoBDHJVz9fE+9LFUGCsUmS2Jgg==", - "type": "package", - "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple/4.3.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.4.3.1.nupkg.sha512", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.nuspec", - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.Apple.dylib" - ] - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "6mU/cVmmHtQiDXhnzUImxIcDL48GbTk+TsptXyJA+MIOG9LRjPoAQC/qBFB7X+UNyK86bmvGwC8t+M66wsYC8w==", - "type": "package", - "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.OpenSsl.dylib" - ] - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "vjwG0GGcTW/PPg6KVud8F9GLWYuAV1rrw1BKAqY0oh4jcUqg15oYF1+qkGR2x2ZHM4DQnWKQ7cJgYbfncz/lYg==", - "type": "package", - "path": "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/rhel.7-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "7KMFpTkHC/zoExs+PwP8jDCWcrK9H6L7soowT80CUx3e+nxP/AFnq0AQAW5W76z2WYbLAYCRyPfwYFG6zkvQRw==", - "type": "package", - "path": "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/ubuntu.14.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "xrlmRCnKZJLHxyyLIqkZjNXqgxnKdZxfItrPkjI+6pkRo5lHX8YvSZlWrSI5AVwLMi4HbNWP7064hcAWeZKp5w==", - "type": "package", - "path": "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/ubuntu.16.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==", - "type": "package", - "path": "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/ubuntu.16.10-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "Semver/2.0.4": { - "sha512": "H52a9cgrh4hkVxv5y99L9vk2NBtJB3ids/yavTHUvC0pUl3EsWEZAJmjBPFSt7jDtn3Q04+Nhtfo0J2xvsnhZQ==", - "type": "package", - "path": "semver/2.0.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net452/Semver.dll", - "lib/net452/Semver.xml", - "lib/netstandard1.1/Semver.dll", - "lib/netstandard1.1/Semver.xml", - "semver.2.0.4.nupkg.sha512", - "semver.nuspec" - ] - }, - "Serilog/2.6.0": { - "sha512": "hxHgHkza8ZowrvQ+jTEF2ypDWGP8NbNtxPQIx7ogTAM9fisDFo2+6kOaxCkleZLO8hiyq0kW5tDLkgm/dbj9uA==", - "type": "package", - "path": "serilog/2.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.dll", - "lib/net45/Serilog.xml", - "lib/net46/Serilog.dll", - "lib/net46/Serilog.xml", - "lib/netstandard1.0/Serilog.dll", - "lib/netstandard1.0/Serilog.xml", - "lib/netstandard1.3/Serilog.dll", - "lib/netstandard1.3/Serilog.xml", - "serilog.2.6.0.nupkg.sha512", - "serilog.nuspec" - ] - }, - "Serilog.Enrichers.Environment/2.1.3": { - "sha512": "LclB09MAKLU14PXxlPiiejZxer1JhtbaVd1Zlb4EgllPYhzePYljx/jqW5uIXBe89hTtGlsCxjHLg9H6s1U24Q==", - "type": "package", - "path": "serilog.enrichers.environment/2.1.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.Enrichers.Environment.dll", - "lib/netstandard1.3/Serilog.Enrichers.Environment.dll", - "lib/netstandard1.5/Serilog.Enrichers.Environment.dll", - "serilog.enrichers.environment.2.1.3.nupkg.sha512", - "serilog.enrichers.environment.nuspec" - ] - }, - "Serilog.Enrichers.Thread/3.1.0": { - "sha512": "85lWsGRJpRxvKT6j/H67no55SUBsBIvp556TKuBTGhjtoPeq+L7j/sDWbgAtvT0p7u7/phJyX6j35PQ4Vtqw0g==", - "type": "package", - "path": "serilog.enrichers.thread/3.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.Enrichers.Thread.dll", - "lib/net45/Serilog.Enrichers.Thread.xml", - "lib/netstandard1.0/Serilog.Enrichers.Thread.dll", - "lib/netstandard1.0/Serilog.Enrichers.Thread.xml", - "lib/netstandard2.0/Serilog.Enrichers.Thread.dll", - "lib/netstandard2.0/Serilog.Enrichers.Thread.xml", - "serilog.enrichers.thread.3.1.0.nupkg.sha512", - "serilog.enrichers.thread.nuspec" - ] - }, - "Serilog.Extensions.Logging/2.0.4": { - "sha512": "C8Vf9Wj1M+wGilChTV+OhE4v5ZCDzQfHjLKj2yNDMkXf/zgUKeAUZfbrVrt/c+flXP8M7/SHWBOXTkuPgubFsA==", - "type": "package", - "path": "serilog.extensions.logging/2.0.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.Extensions.Logging.dll", - "lib/net45/Serilog.Extensions.Logging.xml", - "lib/net46/Serilog.Extensions.Logging.dll", - "lib/net46/Serilog.Extensions.Logging.xml", - "lib/net461/Serilog.Extensions.Logging.dll", - "lib/net461/Serilog.Extensions.Logging.xml", - "lib/netstandard1.3/Serilog.Extensions.Logging.dll", - "lib/netstandard1.3/Serilog.Extensions.Logging.xml", - "lib/netstandard2.0/Serilog.Extensions.Logging.dll", - "lib/netstandard2.0/Serilog.Extensions.Logging.xml", - "serilog.extensions.logging.2.0.4.nupkg.sha512", - "serilog.extensions.logging.nuspec" - ] - }, - "Serilog.Settings.Configuration/3.1.0": { - "sha512": "BS+G1dhThTHBOYm8R21JNlR+Nh7ETAOlJuL1P6te1rOG98eV1vos5EyWRTGr0AbHgySxsGu1Q/evfFxS9+Gk1Q==", - "type": "package", - "path": "serilog.settings.configuration/3.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net451/Serilog.Settings.Configuration.dll", - "lib/net451/Serilog.Settings.Configuration.xml", - "lib/net461/Serilog.Settings.Configuration.dll", - "lib/net461/Serilog.Settings.Configuration.xml", - "lib/netstandard2.0/Serilog.Settings.Configuration.dll", - "lib/netstandard2.0/Serilog.Settings.Configuration.xml", - "serilog.settings.configuration.3.1.0.nupkg.sha512", - "serilog.settings.configuration.nuspec" - ] - }, - "Serilog.Sinks.Console/3.1.1": { - "sha512": "56mI5AqvyF/i/c2451nvV71kq370XOCE4Uu5qiaJ295sOhMb9q3BWwG7mWLOVSnmpWiq0SBT3SXfgRXGNP6vzA==", - "type": "package", - "path": "serilog.sinks.console/3.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.Sinks.Console.dll", - "lib/net45/Serilog.Sinks.Console.xml", - "lib/netcoreapp1.1/Serilog.Sinks.Console.dll", - "lib/netcoreapp1.1/Serilog.Sinks.Console.xml", - "lib/netstandard1.3/Serilog.Sinks.Console.dll", - "lib/netstandard1.3/Serilog.Sinks.Console.xml", - "serilog.sinks.console.3.1.1.nupkg.sha512", - "serilog.sinks.console.nuspec" - ] - }, - "Serilog.Sinks.File/4.0.0": { - "sha512": "vBj43RkAbeP1dzoPFR2+LfV5GevDRPDq6265JJBv223lMvT9rfdwe/S/I9ow7aZSLYKfw4qPDw6NW8YwjbDbvg==", - "type": "package", - "path": "serilog.sinks.file/4.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.Sinks.File.dll", - "lib/net45/Serilog.Sinks.File.xml", - "lib/netstandard1.3/Serilog.Sinks.File.dll", - "lib/netstandard1.3/Serilog.Sinks.File.xml", - "serilog.sinks.file.4.0.0.nupkg.sha512", - "serilog.sinks.file.nuspec" - ] - }, - "SharpRepository.Ioc.Autofac/2.0.4.2": { - "sha512": "hYQyngQI3vEvDge/v0F88ql2NR18+r1YhV68yAifBMR/LXp3J9GLVlFjNUS8Q/Bsopyfq/U9Nd4ji54E7ssq0g==", - "type": "package", - "path": "sharprepository.ioc.autofac/2.0.4.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net451/SharpRepository.Ioc.Autofac.dll", - "lib/netstandard1.3/SharpRepository.Ioc.Autofac.dll", - "sharprepository.ioc.autofac.2.0.4.2.nupkg.sha512", - "sharprepository.ioc.autofac.nuspec" - ] - }, - "SharpRepository.Repository/2.0.4.6": { - "sha512": "vEu3Rp1XAqv7NorkigOhwfkFEi1Dhf+R5xUPVNv4C7/drgO2QCD4mf4sg66D/nkY1KJAP9VfsjEwPQOjycMMJg==", - "type": "package", - "path": "sharprepository.repository/2.0.4.6", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "content/repository.default.json", - "contentFiles/any/net451/repository.default.json", - "contentFiles/any/netstandard1.3/repository.default.json", - "contentFiles/any/netstandard2.0/repository.default.json", - "lib/net451/SharpRepository.Repository.dll", - "lib/netstandard1.3/SharpRepository.Repository.dll", - "lib/netstandard2.0/SharpRepository.Repository.dll", - "sharprepository.repository.2.0.4.6.nupkg.sha512", - "sharprepository.repository.nuspec" - ] - }, - "SharpRepository.XmlRepository/2.0.1-alpha3": { - "sha512": "k0Lvd0EJgvcf4ohY3+NKWJ7td/n+qDRZZD2S3vwE3KgaXUwRzA+kTD9kVrXa9xogoQeBB69AuxpD5cKWEDj66Q==", - "type": "package", - "path": "sharprepository.xmlrepository/2.0.1-alpha3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "content/repository.xml.json", - "contentFiles/any/net451/repository.xml.json", - "contentFiles/any/netstandard2.0/repository.xml.json", - "lib/net451/SharpRepository.XmlRepository.dll", - "lib/netstandard2.0/SharpRepository.XmlRepository.dll", - "sharprepository.xmlrepository.2.0.1-alpha3.nupkg.sha512", - "sharprepository.xmlrepository.nuspec" - ] - }, - "SharpZipLib/1.0.0": { - "sha512": "YuYztmY3jEb21F6e5LIPHJjApdtzdCPQ284UzsCKNfkgW71bukFHJES6RbKi+wm053XzFg0LX5/2vj/9gl8F/g==", - "type": "package", - "path": "sharpziplib/1.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/ICSharpCode.SharpZipLib.dll", - "lib/net45/ICSharpCode.SharpZipLib.xml", - "lib/netstandard2.0/ICSharpCode.SharpZipLib.dll", - "lib/netstandard2.0/ICSharpCode.SharpZipLib.xml", - "sharpziplib.1.0.0.nupkg.sha512", - "sharpziplib.nuspec" - ] - }, - "SimpleBase/1.3.1": { - "sha512": "TqqQc+WFg3s10/PdnI2Dio0U7xEmVXhkf+ey4tOGZzjyd054rnNOLNVahJhMYmfBQdS7/jkVqC8xzLm/bkR3EA==", - "type": "package", - "path": "simplebase/1.3.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/SimpleBase.dll", - "lib/net45/SimpleBase.pdb", - "lib/netstandard1.3/SimpleBase.dll", - "lib/netstandard1.3/SimpleBase.pdb", - "simplebase.1.3.1.nupkg.sha512", - "simplebase.nuspec" - ] - }, - "System.AppContext/4.3.0": { - "sha512": "DW6mMAYwRwj+rizAWQ0s3Zkye2giEIIrsoA6yEL99NjVcXDXlHwAbuxLVofJQnaEeKfsEJdFRy85RtIwcySD6A==", - "type": "package", - "path": "system.appcontext/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.AppContext.dll", - "lib/net463/System.AppContext.dll", - "lib/netcore50/System.AppContext.dll", - "lib/netstandard1.6/System.AppContext.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.AppContext.dll", - "ref/net463/System.AppContext.dll", - "ref/netstandard/_._", - "ref/netstandard1.3/System.AppContext.dll", - "ref/netstandard1.3/System.AppContext.xml", - "ref/netstandard1.3/de/System.AppContext.xml", - "ref/netstandard1.3/es/System.AppContext.xml", - "ref/netstandard1.3/fr/System.AppContext.xml", - "ref/netstandard1.3/it/System.AppContext.xml", - "ref/netstandard1.3/ja/System.AppContext.xml", - "ref/netstandard1.3/ko/System.AppContext.xml", - "ref/netstandard1.3/ru/System.AppContext.xml", - "ref/netstandard1.3/zh-hans/System.AppContext.xml", - "ref/netstandard1.3/zh-hant/System.AppContext.xml", - "ref/netstandard1.6/System.AppContext.dll", - "ref/netstandard1.6/System.AppContext.xml", - "ref/netstandard1.6/de/System.AppContext.xml", - "ref/netstandard1.6/es/System.AppContext.xml", - "ref/netstandard1.6/fr/System.AppContext.xml", - "ref/netstandard1.6/it/System.AppContext.xml", - "ref/netstandard1.6/ja/System.AppContext.xml", - "ref/netstandard1.6/ko/System.AppContext.xml", - "ref/netstandard1.6/ru/System.AppContext.xml", - "ref/netstandard1.6/zh-hans/System.AppContext.xml", - "ref/netstandard1.6/zh-hant/System.AppContext.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.AppContext.dll", - "system.appcontext.4.3.0.nupkg.sha512", - "system.appcontext.nuspec" - ] - }, - "System.Buffers/4.5.0": { - "sha512": "09yL/QiPEDZI9JLg0R0OcGe/0ycFm69QN16DCvXkqkIorCC1Y2EKwk5KvSlfMmGse+LcKkD3H+Cra7fFkxHXEg==", - "type": "package", - "path": "system.buffers/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netcoreapp2.0/_._", - "lib/netstandard1.1/System.Buffers.dll", - "lib/netstandard1.1/System.Buffers.xml", - "lib/netstandard2.0/System.Buffers.dll", - "lib/netstandard2.0/System.Buffers.xml", - "lib/uap10.0.16299/_._", - "ref/net45/System.Buffers.dll", - "ref/net45/System.Buffers.xml", - "ref/netcoreapp2.0/_._", - "ref/netstandard1.1/System.Buffers.dll", - "ref/netstandard1.1/System.Buffers.xml", - "ref/netstandard2.0/System.Buffers.dll", - "ref/netstandard2.0/System.Buffers.xml", - "ref/uap10.0.16299/_._", - "system.buffers.4.5.0.nupkg.sha512", - "system.buffers.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Collections/4.3.0": { - "sha512": "ynuVLTDaFIfKTkOqUigXte4m5+EFNwYoEBEvxnp1EnZsOdQC85S7BCbREIu8+bu2Tpzh9a9zbvIVpRo15V8FGw==", - "type": "package", - "path": "system.collections/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Collections.dll", - "ref/netcore50/System.Collections.xml", - "ref/netcore50/de/System.Collections.xml", - "ref/netcore50/es/System.Collections.xml", - "ref/netcore50/fr/System.Collections.xml", - "ref/netcore50/it/System.Collections.xml", - "ref/netcore50/ja/System.Collections.xml", - "ref/netcore50/ko/System.Collections.xml", - "ref/netcore50/ru/System.Collections.xml", - "ref/netcore50/zh-hans/System.Collections.xml", - "ref/netcore50/zh-hant/System.Collections.xml", - "ref/netstandard1.0/System.Collections.dll", - "ref/netstandard1.0/System.Collections.xml", - "ref/netstandard1.0/de/System.Collections.xml", - "ref/netstandard1.0/es/System.Collections.xml", - "ref/netstandard1.0/fr/System.Collections.xml", - "ref/netstandard1.0/it/System.Collections.xml", - "ref/netstandard1.0/ja/System.Collections.xml", - "ref/netstandard1.0/ko/System.Collections.xml", - "ref/netstandard1.0/ru/System.Collections.xml", - "ref/netstandard1.0/zh-hans/System.Collections.xml", - "ref/netstandard1.0/zh-hant/System.Collections.xml", - "ref/netstandard1.3/System.Collections.dll", - "ref/netstandard1.3/System.Collections.xml", - "ref/netstandard1.3/de/System.Collections.xml", - "ref/netstandard1.3/es/System.Collections.xml", - "ref/netstandard1.3/fr/System.Collections.xml", - "ref/netstandard1.3/it/System.Collections.xml", - "ref/netstandard1.3/ja/System.Collections.xml", - "ref/netstandard1.3/ko/System.Collections.xml", - "ref/netstandard1.3/ru/System.Collections.xml", - "ref/netstandard1.3/zh-hans/System.Collections.xml", - "ref/netstandard1.3/zh-hant/System.Collections.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.collections.4.3.0.nupkg.sha512", - "system.collections.nuspec" - ] - }, - "System.Collections.Concurrent/4.3.0": { - "sha512": "NcGqPmNiFv5dwuvrUEKT5prWNV0m4iRTrwYK+U2CefqpO9z+EnrssLMWx+fZGFvKxy6ZSYTv238thRXx9Vz2gg==", - "type": "package", - "path": "system.collections.concurrent/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Collections.Concurrent.dll", - "lib/netstandard1.3/System.Collections.Concurrent.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Collections.Concurrent.dll", - "ref/netcore50/System.Collections.Concurrent.xml", - "ref/netcore50/de/System.Collections.Concurrent.xml", - "ref/netcore50/es/System.Collections.Concurrent.xml", - "ref/netcore50/fr/System.Collections.Concurrent.xml", - "ref/netcore50/it/System.Collections.Concurrent.xml", - "ref/netcore50/ja/System.Collections.Concurrent.xml", - "ref/netcore50/ko/System.Collections.Concurrent.xml", - "ref/netcore50/ru/System.Collections.Concurrent.xml", - "ref/netcore50/zh-hans/System.Collections.Concurrent.xml", - "ref/netcore50/zh-hant/System.Collections.Concurrent.xml", - "ref/netstandard1.1/System.Collections.Concurrent.dll", - "ref/netstandard1.1/System.Collections.Concurrent.xml", - "ref/netstandard1.1/de/System.Collections.Concurrent.xml", - "ref/netstandard1.1/es/System.Collections.Concurrent.xml", - "ref/netstandard1.1/fr/System.Collections.Concurrent.xml", - "ref/netstandard1.1/it/System.Collections.Concurrent.xml", - "ref/netstandard1.1/ja/System.Collections.Concurrent.xml", - "ref/netstandard1.1/ko/System.Collections.Concurrent.xml", - "ref/netstandard1.1/ru/System.Collections.Concurrent.xml", - "ref/netstandard1.1/zh-hans/System.Collections.Concurrent.xml", - "ref/netstandard1.1/zh-hant/System.Collections.Concurrent.xml", - "ref/netstandard1.3/System.Collections.Concurrent.dll", - "ref/netstandard1.3/System.Collections.Concurrent.xml", - "ref/netstandard1.3/de/System.Collections.Concurrent.xml", - "ref/netstandard1.3/es/System.Collections.Concurrent.xml", - "ref/netstandard1.3/fr/System.Collections.Concurrent.xml", - "ref/netstandard1.3/it/System.Collections.Concurrent.xml", - "ref/netstandard1.3/ja/System.Collections.Concurrent.xml", - "ref/netstandard1.3/ko/System.Collections.Concurrent.xml", - "ref/netstandard1.3/ru/System.Collections.Concurrent.xml", - "ref/netstandard1.3/zh-hans/System.Collections.Concurrent.xml", - "ref/netstandard1.3/zh-hant/System.Collections.Concurrent.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.collections.concurrent.4.3.0.nupkg.sha512", - "system.collections.concurrent.nuspec" - ] - }, - "System.Collections.Immutable/1.5.0": { - "sha512": "T5XGQlcHhEO75Qx38GGCUDPdk4n/7yrRmTgy4yczzJV8alPHaxPU55TBC2UFrkQ42bu34sZPfK0dU+nWZUOEJA==", - "type": "package", - "path": "system.collections.immutable/1.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Collections.Immutable.dll", - "lib/netstandard1.0/System.Collections.Immutable.xml", - "lib/netstandard1.3/System.Collections.Immutable.dll", - "lib/netstandard1.3/System.Collections.Immutable.xml", - "lib/netstandard2.0/System.Collections.Immutable.dll", - "lib/netstandard2.0/System.Collections.Immutable.xml", - "lib/portable-net45+win8+wp8+wpa81/System.Collections.Immutable.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Collections.Immutable.xml", - "system.collections.immutable.1.5.0.nupkg.sha512", - "system.collections.immutable.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Collections.NonGeneric/4.0.1": { - "sha512": "hMxFT2RhhlffyCdKLDXjx8WEC5JfCvNozAZxCablAuFRH74SCV4AgzE8yJCh/73bFnEoZgJ9MJmkjQ0dJmnKqA==", - "type": "package", - "path": "system.collections.nongeneric/4.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Collections.NonGeneric.dll", - "lib/netstandard1.3/System.Collections.NonGeneric.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Collections.NonGeneric.dll", - "ref/netstandard1.3/System.Collections.NonGeneric.dll", - "ref/netstandard1.3/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/de/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/es/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/fr/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/it/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/ja/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/ko/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/ru/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/zh-hans/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/zh-hant/System.Collections.NonGeneric.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.collections.nongeneric.4.0.1.nupkg.sha512", - "system.collections.nongeneric.nuspec" - ] - }, - "System.ComponentModel/4.0.1": { - "sha512": "oBZFnm7seFiVfugsIyOvQCWobNZs7FzqDV/B7tx20Ep/l3UUFCPDkdTnCNaJZTU27zjeODmy2C/cP60u3D4c9w==", - "type": "package", - "path": "system.componentmodel/4.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.ComponentModel.dll", - "lib/netstandard1.3/System.ComponentModel.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.ComponentModel.dll", - "ref/netcore50/System.ComponentModel.xml", - "ref/netcore50/de/System.ComponentModel.xml", - "ref/netcore50/es/System.ComponentModel.xml", - "ref/netcore50/fr/System.ComponentModel.xml", - "ref/netcore50/it/System.ComponentModel.xml", - "ref/netcore50/ja/System.ComponentModel.xml", - "ref/netcore50/ko/System.ComponentModel.xml", - "ref/netcore50/ru/System.ComponentModel.xml", - "ref/netcore50/zh-hans/System.ComponentModel.xml", - "ref/netcore50/zh-hant/System.ComponentModel.xml", - "ref/netstandard1.0/System.ComponentModel.dll", - "ref/netstandard1.0/System.ComponentModel.xml", - "ref/netstandard1.0/de/System.ComponentModel.xml", - "ref/netstandard1.0/es/System.ComponentModel.xml", - "ref/netstandard1.0/fr/System.ComponentModel.xml", - "ref/netstandard1.0/it/System.ComponentModel.xml", - "ref/netstandard1.0/ja/System.ComponentModel.xml", - "ref/netstandard1.0/ko/System.ComponentModel.xml", - "ref/netstandard1.0/ru/System.ComponentModel.xml", - "ref/netstandard1.0/zh-hans/System.ComponentModel.xml", - "ref/netstandard1.0/zh-hant/System.ComponentModel.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.componentmodel.4.0.1.nupkg.sha512", - "system.componentmodel.nuspec" - ] - }, - "System.Composition/1.2.0": { - "sha512": "nTgIj77StlLM7CW3uFM3B/0Yen5udzaeSQcdSCVV3wIlRGYsXYLjZWTYa9m8IBjQiyZKsukKYaogqhOa6QUlDA==", - "type": "package", - "path": "system.composition/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "system.composition.1.2.0.nupkg.sha512", - "system.composition.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Composition.AttributedModel/1.2.0": { - "sha512": "IQ2bn1BR/Q7gapjnXR/HGh0BMtjYVU0t0uPZ3LXE4yfwjM7x/HcImJxwwhUtnL+YWU5/pTOhzZnqsjwKJpWaug==", - "type": "package", - "path": "system.composition.attributedmodel/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Composition.AttributedModel.dll", - "lib/netstandard2.0/System.Composition.AttributedModel.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Composition.AttributedModel.dll", - "system.composition.attributedmodel.1.2.0.nupkg.sha512", - "system.composition.attributedmodel.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Composition.Convention/1.2.0": { - "sha512": "g9PSAdL/0dT3GZbdwt5r238RLHfnn+ujRVhoOGvVNjbbhlgZeKcDA+zsje4Y81csMywAPsDXkeXrBigtjINurg==", - "type": "package", - "path": "system.composition.convention/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Composition.Convention.dll", - "lib/netstandard2.0/System.Composition.Convention.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Composition.Convention.dll", - "system.composition.convention.1.2.0.nupkg.sha512", - "system.composition.convention.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Composition.Hosting/1.2.0": { - "sha512": "NQa4OanHFuWVpMuj3+0RnoAq2v+5KQNA3+EYuhmuDbOfR06o7rYjzs9FHP0XWJWN85vqnM76dgAgj46OYsDV8A==", - "type": "package", - "path": "system.composition.hosting/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Composition.Hosting.dll", - "lib/netstandard2.0/System.Composition.Hosting.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Composition.Hosting.dll", - "system.composition.hosting.1.2.0.nupkg.sha512", - "system.composition.hosting.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Composition.Runtime/1.2.0": { - "sha512": "F8Ef3y9/JKbK4lEqJScFnfhT8/CwboGS890a/Js9E11wb1N6rl63pU8wxRPmy2MUUUHSafxrF3ooIh94pNEF0g==", - "type": "package", - "path": "system.composition.runtime/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Composition.Runtime.dll", - "lib/netstandard2.0/System.Composition.Runtime.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Composition.Runtime.dll", - "system.composition.runtime.1.2.0.nupkg.sha512", - "system.composition.runtime.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Composition.TypedParts/1.2.0": { - "sha512": "cLjoUGnaLRkJSwL6FLEx3aJanDgwEtyoEqf9cE6Z5ipbjNXAlk7W11uwNfHaECxdPa/QfGbvaRd4i24gxc5ygg==", - "type": "package", - "path": "system.composition.typedparts/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Composition.TypedParts.dll", - "lib/netstandard2.0/System.Composition.TypedParts.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Composition.TypedParts.dll", - "system.composition.typedparts.1.2.0.nupkg.sha512", - "system.composition.typedparts.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Console/4.3.0": { - "sha512": "oIpoSlg8mzJ4zjK+EAfa5JX52HJUZmOS95TvEgMHnzM819OIwolE/6NvtJ8Mi7IfQscPbh18HAOSDfbQ0RMMgg==", - "type": "package", - "path": "system.console/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Console.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Console.dll", - "ref/netstandard1.3/System.Console.dll", - "ref/netstandard1.3/System.Console.xml", - "ref/netstandard1.3/de/System.Console.xml", - "ref/netstandard1.3/es/System.Console.xml", - "ref/netstandard1.3/fr/System.Console.xml", - "ref/netstandard1.3/it/System.Console.xml", - "ref/netstandard1.3/ja/System.Console.xml", - "ref/netstandard1.3/ko/System.Console.xml", - "ref/netstandard1.3/ru/System.Console.xml", - "ref/netstandard1.3/zh-hans/System.Console.xml", - "ref/netstandard1.3/zh-hant/System.Console.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.console.4.3.0.nupkg.sha512", - "system.console.nuspec" - ] - }, - "System.Diagnostics.Contracts/4.3.0": { - "sha512": "hQjC+dSe1rBacyz3Qh4hooO4bWhG3/pzIL4bsMHPsLCWAQ5xfNYIuB7sU7pVZSV2v/p7DcrX6U7qAjZKJSaigg==", - "type": "package", - "path": "system.diagnostics.contracts/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Diagnostics.Contracts.dll", - "lib/netstandard1.0/System.Diagnostics.Contracts.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Diagnostics.Contracts.dll", - "ref/netcore50/System.Diagnostics.Contracts.xml", - "ref/netcore50/de/System.Diagnostics.Contracts.xml", - "ref/netcore50/es/System.Diagnostics.Contracts.xml", - "ref/netcore50/fr/System.Diagnostics.Contracts.xml", - "ref/netcore50/it/System.Diagnostics.Contracts.xml", - "ref/netcore50/ja/System.Diagnostics.Contracts.xml", - "ref/netcore50/ko/System.Diagnostics.Contracts.xml", - "ref/netcore50/ru/System.Diagnostics.Contracts.xml", - "ref/netcore50/zh-hans/System.Diagnostics.Contracts.xml", - "ref/netcore50/zh-hant/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/System.Diagnostics.Contracts.dll", - "ref/netstandard1.0/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/de/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/es/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/fr/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/it/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/ja/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/ko/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/ru/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/zh-hans/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/zh-hant/System.Diagnostics.Contracts.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Diagnostics.Contracts.dll", - "system.diagnostics.contracts.4.3.0.nupkg.sha512", - "system.diagnostics.contracts.nuspec" - ] - }, - "System.Diagnostics.Debug/4.3.0": { - "sha512": "bFj+HjYY5/h2hMHOp+/H07Gb19+NJTX54ntixS9EHxG2eyEiXWvNYvQJ4CwqFiMcTbGb4zuPq1ubClyGYN2rJA==", - "type": "package", - "path": "system.diagnostics.debug/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Diagnostics.Debug.dll", - "ref/netcore50/System.Diagnostics.Debug.xml", - "ref/netcore50/de/System.Diagnostics.Debug.xml", - "ref/netcore50/es/System.Diagnostics.Debug.xml", - "ref/netcore50/fr/System.Diagnostics.Debug.xml", - "ref/netcore50/it/System.Diagnostics.Debug.xml", - "ref/netcore50/ja/System.Diagnostics.Debug.xml", - "ref/netcore50/ko/System.Diagnostics.Debug.xml", - "ref/netcore50/ru/System.Diagnostics.Debug.xml", - "ref/netcore50/zh-hans/System.Diagnostics.Debug.xml", - "ref/netcore50/zh-hant/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/System.Diagnostics.Debug.dll", - "ref/netstandard1.0/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/de/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/es/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/fr/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/it/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/ja/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/ko/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/ru/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/zh-hans/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/zh-hant/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/System.Diagnostics.Debug.dll", - "ref/netstandard1.3/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/de/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/es/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/fr/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/it/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/ja/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/ko/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/ru/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/zh-hans/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/zh-hant/System.Diagnostics.Debug.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.diagnostics.debug.4.3.0.nupkg.sha512", - "system.diagnostics.debug.nuspec" - ] - }, - "System.Diagnostics.DiagnosticSource/4.3.0": { - "sha512": "j1TfX/OCtmUOLhDRBDhjokv0n/BxTneeg3zXBz5G+yY1vPzc+Z18Sp2V816//YarDKBoMF9LJFBy4IMDuRezTQ==", - "type": "package", - "path": "system.diagnostics.diagnosticsource/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/net46/System.Diagnostics.DiagnosticSource.dll", - "lib/net46/System.Diagnostics.DiagnosticSource.xml", - "lib/netstandard1.1/System.Diagnostics.DiagnosticSource.dll", - "lib/netstandard1.1/System.Diagnostics.DiagnosticSource.xml", - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll", - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.xml", - "lib/portable-net45+win8+wpa81/System.Diagnostics.DiagnosticSource.dll", - "lib/portable-net45+win8+wpa81/System.Diagnostics.DiagnosticSource.xml", - "system.diagnostics.diagnosticsource.4.3.0.nupkg.sha512", - "system.diagnostics.diagnosticsource.nuspec" - ] - }, - "System.Diagnostics.Process/4.1.0": { - "sha512": "mpVZ5bnlSs3tTeJ6jYyDJEIa6tavhAd88lxq1zbYhkkCu0Pno2+gHXcvZcoygq2d8JxW3gojXqNJMTAshduqZA==", - "type": "package", - "path": "system.diagnostics.process/4.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Diagnostics.Process.dll", - "lib/net461/System.Diagnostics.Process.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Diagnostics.Process.dll", - "ref/net461/System.Diagnostics.Process.dll", - "ref/netstandard1.3/System.Diagnostics.Process.dll", - "ref/netstandard1.3/System.Diagnostics.Process.xml", - "ref/netstandard1.3/de/System.Diagnostics.Process.xml", - "ref/netstandard1.3/es/System.Diagnostics.Process.xml", - "ref/netstandard1.3/fr/System.Diagnostics.Process.xml", - "ref/netstandard1.3/it/System.Diagnostics.Process.xml", - "ref/netstandard1.3/ja/System.Diagnostics.Process.xml", - "ref/netstandard1.3/ko/System.Diagnostics.Process.xml", - "ref/netstandard1.3/ru/System.Diagnostics.Process.xml", - "ref/netstandard1.3/zh-hans/System.Diagnostics.Process.xml", - "ref/netstandard1.3/zh-hant/System.Diagnostics.Process.xml", - "ref/netstandard1.4/System.Diagnostics.Process.dll", - "ref/netstandard1.4/System.Diagnostics.Process.xml", - "ref/netstandard1.4/de/System.Diagnostics.Process.xml", - "ref/netstandard1.4/es/System.Diagnostics.Process.xml", - "ref/netstandard1.4/fr/System.Diagnostics.Process.xml", - "ref/netstandard1.4/it/System.Diagnostics.Process.xml", - "ref/netstandard1.4/ja/System.Diagnostics.Process.xml", - "ref/netstandard1.4/ko/System.Diagnostics.Process.xml", - "ref/netstandard1.4/ru/System.Diagnostics.Process.xml", - "ref/netstandard1.4/zh-hans/System.Diagnostics.Process.xml", - "ref/netstandard1.4/zh-hant/System.Diagnostics.Process.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/linux/lib/netstandard1.4/System.Diagnostics.Process.dll", - "runtimes/osx/lib/netstandard1.4/System.Diagnostics.Process.dll", - "runtimes/win/lib/net46/System.Diagnostics.Process.dll", - "runtimes/win/lib/net461/System.Diagnostics.Process.dll", - "runtimes/win/lib/netstandard1.4/System.Diagnostics.Process.dll", - "runtimes/win7/lib/netcore50/_._", - "system.diagnostics.process.4.1.0.nupkg.sha512", - "system.diagnostics.process.nuspec" - ] - }, - "System.Diagnostics.StackTrace/4.3.0": { - "sha512": "On2V/V1k2LSQwS1+kMIrLUdsJay3ohG5IFYm1qkALFEHqsGo79CCFxgUc+CS5qveFc+ys1zO6G4YvRu3/tLL6A==", - "type": "package", - "path": "system.diagnostics.stacktrace/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Diagnostics.StackTrace.dll", - "lib/netstandard1.3/System.Diagnostics.StackTrace.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Diagnostics.StackTrace.dll", - "ref/netstandard1.3/System.Diagnostics.StackTrace.dll", - "ref/netstandard1.3/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/de/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/es/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/fr/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/it/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/ja/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/ko/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/ru/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/zh-hans/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/zh-hant/System.Diagnostics.StackTrace.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Diagnostics.StackTrace.dll", - "system.diagnostics.stacktrace.4.3.0.nupkg.sha512", - "system.diagnostics.stacktrace.nuspec" - ] - }, - "System.Diagnostics.Tools/4.3.0": { - "sha512": "Fk1pd+chy860Tt57/XWwO42XceBCau+l1Axxhn6WQJL9xqaAi8vFVZ7XPsLFMsplfWR2r3mknKOth5uDZvE9kA==", - "type": "package", - "path": "system.diagnostics.tools/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Diagnostics.Tools.dll", - "ref/netcore50/System.Diagnostics.Tools.xml", - "ref/netcore50/de/System.Diagnostics.Tools.xml", - "ref/netcore50/es/System.Diagnostics.Tools.xml", - "ref/netcore50/fr/System.Diagnostics.Tools.xml", - "ref/netcore50/it/System.Diagnostics.Tools.xml", - "ref/netcore50/ja/System.Diagnostics.Tools.xml", - "ref/netcore50/ko/System.Diagnostics.Tools.xml", - "ref/netcore50/ru/System.Diagnostics.Tools.xml", - "ref/netcore50/zh-hans/System.Diagnostics.Tools.xml", - "ref/netcore50/zh-hant/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/System.Diagnostics.Tools.dll", - "ref/netstandard1.0/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/de/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/es/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/fr/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/it/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/ja/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/ko/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/ru/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/zh-hans/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/zh-hant/System.Diagnostics.Tools.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.diagnostics.tools.4.3.0.nupkg.sha512", - "system.diagnostics.tools.nuspec" - ] - }, - "System.Diagnostics.Tracing/4.3.0": { - "sha512": "0KXTDiYc1Ft9+rArf/vXa2TgybiS7YJuphSByYPAIIsFtpmBzXnpHNTlgR4c1MPOoGoa/OBYEezli+XkwgFp6g==", - "type": "package", - "path": "system.diagnostics.tracing/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Diagnostics.Tracing.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Diagnostics.Tracing.dll", - "ref/netcore50/System.Diagnostics.Tracing.dll", - "ref/netcore50/System.Diagnostics.Tracing.xml", - "ref/netcore50/de/System.Diagnostics.Tracing.xml", - "ref/netcore50/es/System.Diagnostics.Tracing.xml", - "ref/netcore50/fr/System.Diagnostics.Tracing.xml", - "ref/netcore50/it/System.Diagnostics.Tracing.xml", - "ref/netcore50/ja/System.Diagnostics.Tracing.xml", - "ref/netcore50/ko/System.Diagnostics.Tracing.xml", - "ref/netcore50/ru/System.Diagnostics.Tracing.xml", - "ref/netcore50/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netcore50/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/System.Diagnostics.Tracing.dll", - "ref/netstandard1.1/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/System.Diagnostics.Tracing.dll", - "ref/netstandard1.2/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/System.Diagnostics.Tracing.dll", - "ref/netstandard1.3/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/System.Diagnostics.Tracing.dll", - "ref/netstandard1.5/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/zh-hant/System.Diagnostics.Tracing.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.diagnostics.tracing.4.3.0.nupkg.sha512", - "system.diagnostics.tracing.nuspec" - ] - }, - "System.Dynamic.Runtime/4.0.11": { - "sha512": "CyGJpvUO/6tEqLH4g/Kh+bmzLESBIxkOiUaod8KP9GojWqkK8ImNHM1tovMVWqLPJuV/f2HufjxQ394hkNeBqw==", - "type": "package", - "path": "system.dynamic.runtime/4.0.11", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Dynamic.Runtime.dll", - "lib/netstandard1.3/System.Dynamic.Runtime.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Dynamic.Runtime.dll", - "ref/netcore50/System.Dynamic.Runtime.xml", - "ref/netcore50/de/System.Dynamic.Runtime.xml", - "ref/netcore50/es/System.Dynamic.Runtime.xml", - "ref/netcore50/fr/System.Dynamic.Runtime.xml", - "ref/netcore50/it/System.Dynamic.Runtime.xml", - "ref/netcore50/ja/System.Dynamic.Runtime.xml", - "ref/netcore50/ko/System.Dynamic.Runtime.xml", - "ref/netcore50/ru/System.Dynamic.Runtime.xml", - "ref/netcore50/zh-hans/System.Dynamic.Runtime.xml", - "ref/netcore50/zh-hant/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/System.Dynamic.Runtime.dll", - "ref/netstandard1.0/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/de/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/es/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/fr/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/it/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/ja/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/ko/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/ru/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/zh-hans/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/zh-hant/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/System.Dynamic.Runtime.dll", - "ref/netstandard1.3/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/de/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/es/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/fr/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/it/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/ja/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/ko/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/ru/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/zh-hans/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/zh-hant/System.Dynamic.Runtime.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Dynamic.Runtime.dll", - "system.dynamic.runtime.4.0.11.nupkg.sha512", - "system.dynamic.runtime.nuspec" - ] - }, - "System.Globalization/4.3.0": { - "sha512": "gj0rowjLBztAoxRuzM0Nn9exYVrD+++xb3PYc+QR/YHDvch98gbT3H4vFMnNU6r8poSjVwwlRxKAqtqN6AXs4g==", - "type": "package", - "path": "system.globalization/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Globalization.dll", - "ref/netcore50/System.Globalization.xml", - "ref/netcore50/de/System.Globalization.xml", - "ref/netcore50/es/System.Globalization.xml", - "ref/netcore50/fr/System.Globalization.xml", - "ref/netcore50/it/System.Globalization.xml", - "ref/netcore50/ja/System.Globalization.xml", - "ref/netcore50/ko/System.Globalization.xml", - "ref/netcore50/ru/System.Globalization.xml", - "ref/netcore50/zh-hans/System.Globalization.xml", - "ref/netcore50/zh-hant/System.Globalization.xml", - "ref/netstandard1.0/System.Globalization.dll", - "ref/netstandard1.0/System.Globalization.xml", - "ref/netstandard1.0/de/System.Globalization.xml", - "ref/netstandard1.0/es/System.Globalization.xml", - "ref/netstandard1.0/fr/System.Globalization.xml", - "ref/netstandard1.0/it/System.Globalization.xml", - "ref/netstandard1.0/ja/System.Globalization.xml", - "ref/netstandard1.0/ko/System.Globalization.xml", - "ref/netstandard1.0/ru/System.Globalization.xml", - "ref/netstandard1.0/zh-hans/System.Globalization.xml", - "ref/netstandard1.0/zh-hant/System.Globalization.xml", - "ref/netstandard1.3/System.Globalization.dll", - "ref/netstandard1.3/System.Globalization.xml", - "ref/netstandard1.3/de/System.Globalization.xml", - "ref/netstandard1.3/es/System.Globalization.xml", - "ref/netstandard1.3/fr/System.Globalization.xml", - "ref/netstandard1.3/it/System.Globalization.xml", - "ref/netstandard1.3/ja/System.Globalization.xml", - "ref/netstandard1.3/ko/System.Globalization.xml", - "ref/netstandard1.3/ru/System.Globalization.xml", - "ref/netstandard1.3/zh-hans/System.Globalization.xml", - "ref/netstandard1.3/zh-hant/System.Globalization.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.globalization.4.3.0.nupkg.sha512", - "system.globalization.nuspec" - ] - }, - "System.Globalization.Calendars/4.3.0": { - "sha512": "6XGQIxQCs5N3S5Je/AKiv6QdHRF6F/uH2m45n1I0VGlidn6c2POZcO+kCOT0U80eZ1Giph42a8l0BuGwuKS+hg==", - "type": "package", - "path": "system.globalization.calendars/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Globalization.Calendars.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Globalization.Calendars.dll", - "ref/netstandard1.3/System.Globalization.Calendars.dll", - "ref/netstandard1.3/System.Globalization.Calendars.xml", - "ref/netstandard1.3/de/System.Globalization.Calendars.xml", - "ref/netstandard1.3/es/System.Globalization.Calendars.xml", - "ref/netstandard1.3/fr/System.Globalization.Calendars.xml", - "ref/netstandard1.3/it/System.Globalization.Calendars.xml", - "ref/netstandard1.3/ja/System.Globalization.Calendars.xml", - "ref/netstandard1.3/ko/System.Globalization.Calendars.xml", - "ref/netstandard1.3/ru/System.Globalization.Calendars.xml", - "ref/netstandard1.3/zh-hans/System.Globalization.Calendars.xml", - "ref/netstandard1.3/zh-hant/System.Globalization.Calendars.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.globalization.calendars.4.3.0.nupkg.sha512", - "system.globalization.calendars.nuspec" - ] - }, - "System.Globalization.Extensions/4.3.0": { - "sha512": "pNNgAD+V4MMe3znAuR4cc4UKYKxdADKxfbiIo8fXE0zvms2XIZ0UF0rSE7fARPSbNkzFcgBz6/y24b9uTsJM5Q==", - "type": "package", - "path": "system.globalization.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Globalization.Extensions.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Globalization.Extensions.dll", - "ref/netstandard1.3/System.Globalization.Extensions.dll", - "ref/netstandard1.3/System.Globalization.Extensions.xml", - "ref/netstandard1.3/de/System.Globalization.Extensions.xml", - "ref/netstandard1.3/es/System.Globalization.Extensions.xml", - "ref/netstandard1.3/fr/System.Globalization.Extensions.xml", - "ref/netstandard1.3/it/System.Globalization.Extensions.xml", - "ref/netstandard1.3/ja/System.Globalization.Extensions.xml", - "ref/netstandard1.3/ko/System.Globalization.Extensions.xml", - "ref/netstandard1.3/ru/System.Globalization.Extensions.xml", - "ref/netstandard1.3/zh-hans/System.Globalization.Extensions.xml", - "ref/netstandard1.3/zh-hant/System.Globalization.Extensions.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Globalization.Extensions.dll", - "runtimes/win/lib/net46/System.Globalization.Extensions.dll", - "runtimes/win/lib/netstandard1.3/System.Globalization.Extensions.dll", - "system.globalization.extensions.4.3.0.nupkg.sha512", - "system.globalization.extensions.nuspec" - ] - }, - "System.IO/4.3.0": { - "sha512": "v8paIePhmGuXZbE9xvvNb4uJ5ME4OFXR1+8la/G/L1GIl2nbU2WFnddgb79kVK3U2us7q1aZT/uY/R0D/ovB5g==", - "type": "package", - "path": "system.io/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.IO.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.IO.dll", - "ref/netcore50/System.IO.dll", - "ref/netcore50/System.IO.xml", - "ref/netcore50/de/System.IO.xml", - "ref/netcore50/es/System.IO.xml", - "ref/netcore50/fr/System.IO.xml", - "ref/netcore50/it/System.IO.xml", - "ref/netcore50/ja/System.IO.xml", - "ref/netcore50/ko/System.IO.xml", - "ref/netcore50/ru/System.IO.xml", - "ref/netcore50/zh-hans/System.IO.xml", - "ref/netcore50/zh-hant/System.IO.xml", - "ref/netstandard1.0/System.IO.dll", - "ref/netstandard1.0/System.IO.xml", - "ref/netstandard1.0/de/System.IO.xml", - "ref/netstandard1.0/es/System.IO.xml", - "ref/netstandard1.0/fr/System.IO.xml", - "ref/netstandard1.0/it/System.IO.xml", - "ref/netstandard1.0/ja/System.IO.xml", - "ref/netstandard1.0/ko/System.IO.xml", - "ref/netstandard1.0/ru/System.IO.xml", - "ref/netstandard1.0/zh-hans/System.IO.xml", - "ref/netstandard1.0/zh-hant/System.IO.xml", - "ref/netstandard1.3/System.IO.dll", - "ref/netstandard1.3/System.IO.xml", - "ref/netstandard1.3/de/System.IO.xml", - "ref/netstandard1.3/es/System.IO.xml", - "ref/netstandard1.3/fr/System.IO.xml", - "ref/netstandard1.3/it/System.IO.xml", - "ref/netstandard1.3/ja/System.IO.xml", - "ref/netstandard1.3/ko/System.IO.xml", - "ref/netstandard1.3/ru/System.IO.xml", - "ref/netstandard1.3/zh-hans/System.IO.xml", - "ref/netstandard1.3/zh-hant/System.IO.xml", - "ref/netstandard1.5/System.IO.dll", - "ref/netstandard1.5/System.IO.xml", - "ref/netstandard1.5/de/System.IO.xml", - "ref/netstandard1.5/es/System.IO.xml", - "ref/netstandard1.5/fr/System.IO.xml", - "ref/netstandard1.5/it/System.IO.xml", - "ref/netstandard1.5/ja/System.IO.xml", - "ref/netstandard1.5/ko/System.IO.xml", - "ref/netstandard1.5/ru/System.IO.xml", - "ref/netstandard1.5/zh-hans/System.IO.xml", - "ref/netstandard1.5/zh-hant/System.IO.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.4.3.0.nupkg.sha512", - "system.io.nuspec" - ] - }, - "System.IO.Abstractions/6.0.3": { - "sha512": "sGph5mBx7iH7Eab0J9teJ0c+dC7HYIbjgCRzmCM8MIBTaKoHpd9Asp74AAPu/uxMciNjXBj2bY4rxIVXzekBRQ==", - "type": "package", - "path": "system.io.abstractions/6.0.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net40/System.IO.Abstractions.dll", - "lib/net40/System.IO.Abstractions.xml", - "lib/netcoreapp2.0/System.IO.Abstractions.dll", - "lib/netcoreapp2.0/System.IO.Abstractions.xml", - "lib/netstandard1.4/System.IO.Abstractions.dll", - "lib/netstandard1.4/System.IO.Abstractions.xml", - "lib/netstandard2.0/System.IO.Abstractions.dll", - "lib/netstandard2.0/System.IO.Abstractions.xml", - "system.io.abstractions.6.0.3.nupkg.sha512", - "system.io.abstractions.nuspec" - ] - }, - "System.IO.Compression/4.3.0": { - "sha512": "9UDuUaO7aUHN+6rOmpc41/eYai+Udw22H0Wojst+82tXHUwHQX3InNvpZVomK3zFmbCkt47/6pGBnhhhIbRIBw==", - "type": "package", - "path": "system.io.compression/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.IO.Compression.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.IO.Compression.dll", - "ref/netcore50/System.IO.Compression.dll", - "ref/netcore50/System.IO.Compression.xml", - "ref/netcore50/de/System.IO.Compression.xml", - "ref/netcore50/es/System.IO.Compression.xml", - "ref/netcore50/fr/System.IO.Compression.xml", - "ref/netcore50/it/System.IO.Compression.xml", - "ref/netcore50/ja/System.IO.Compression.xml", - "ref/netcore50/ko/System.IO.Compression.xml", - "ref/netcore50/ru/System.IO.Compression.xml", - "ref/netcore50/zh-hans/System.IO.Compression.xml", - "ref/netcore50/zh-hant/System.IO.Compression.xml", - "ref/netstandard1.1/System.IO.Compression.dll", - "ref/netstandard1.1/System.IO.Compression.xml", - "ref/netstandard1.1/de/System.IO.Compression.xml", - "ref/netstandard1.1/es/System.IO.Compression.xml", - "ref/netstandard1.1/fr/System.IO.Compression.xml", - "ref/netstandard1.1/it/System.IO.Compression.xml", - "ref/netstandard1.1/ja/System.IO.Compression.xml", - "ref/netstandard1.1/ko/System.IO.Compression.xml", - "ref/netstandard1.1/ru/System.IO.Compression.xml", - "ref/netstandard1.1/zh-hans/System.IO.Compression.xml", - "ref/netstandard1.1/zh-hant/System.IO.Compression.xml", - "ref/netstandard1.3/System.IO.Compression.dll", - "ref/netstandard1.3/System.IO.Compression.xml", - "ref/netstandard1.3/de/System.IO.Compression.xml", - "ref/netstandard1.3/es/System.IO.Compression.xml", - "ref/netstandard1.3/fr/System.IO.Compression.xml", - "ref/netstandard1.3/it/System.IO.Compression.xml", - "ref/netstandard1.3/ja/System.IO.Compression.xml", - "ref/netstandard1.3/ko/System.IO.Compression.xml", - "ref/netstandard1.3/ru/System.IO.Compression.xml", - "ref/netstandard1.3/zh-hans/System.IO.Compression.xml", - "ref/netstandard1.3/zh-hant/System.IO.Compression.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.IO.Compression.dll", - "runtimes/win/lib/net46/System.IO.Compression.dll", - "runtimes/win/lib/netstandard1.3/System.IO.Compression.dll", - "system.io.compression.4.3.0.nupkg.sha512", - "system.io.compression.nuspec" - ] - }, - "System.IO.Compression.ZipFile/4.3.0": { - "sha512": "GGBjRnJ2f4GPAZLsKydQaT8NOTkPO31ADMb9T250pcvtJ79J5ZgOyF/z4WHDD2GQ9wDjOaEEDBaZuH60qntnkg==", - "type": "package", - "path": "system.io.compression.zipfile/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.IO.Compression.ZipFile.dll", - "lib/netstandard1.3/System.IO.Compression.ZipFile.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.IO.Compression.ZipFile.dll", - "ref/netstandard1.3/System.IO.Compression.ZipFile.dll", - "ref/netstandard1.3/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/de/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/es/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/fr/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/it/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/ja/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/ko/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/ru/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/zh-hans/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/zh-hant/System.IO.Compression.ZipFile.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.compression.zipfile.4.3.0.nupkg.sha512", - "system.io.compression.zipfile.nuspec" - ] - }, - "System.IO.FileSystem/4.3.0": { - "sha512": "T7WB1vhblSmgkaDpdGM3Uqo55Qsr5sip5eyowrwiXOoHBkzOx3ePd9+Zh97r9NzOwFCxqX7awO6RBxQuao7n7g==", - "type": "package", - "path": "system.io.filesystem/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.IO.FileSystem.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.IO.FileSystem.dll", - "ref/netstandard1.3/System.IO.FileSystem.dll", - "ref/netstandard1.3/System.IO.FileSystem.xml", - "ref/netstandard1.3/de/System.IO.FileSystem.xml", - "ref/netstandard1.3/es/System.IO.FileSystem.xml", - "ref/netstandard1.3/fr/System.IO.FileSystem.xml", - "ref/netstandard1.3/it/System.IO.FileSystem.xml", - "ref/netstandard1.3/ja/System.IO.FileSystem.xml", - "ref/netstandard1.3/ko/System.IO.FileSystem.xml", - "ref/netstandard1.3/ru/System.IO.FileSystem.xml", - "ref/netstandard1.3/zh-hans/System.IO.FileSystem.xml", - "ref/netstandard1.3/zh-hant/System.IO.FileSystem.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.filesystem.4.3.0.nupkg.sha512", - "system.io.filesystem.nuspec" - ] - }, - "System.IO.FileSystem.AccessControl/4.5.0": { - "sha512": "TYe6xstoqT5MlTly0OtPU6u9zWuNScLVMEx6sTCjjx+Hqdp0wCXoG6fnzMpTPMQACXQzi9pd2N5Tloow+5jQdQ==", - "type": "package", - "path": "system.io.filesystem.accesscontrol/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net46/System.IO.FileSystem.AccessControl.dll", - "lib/net461/System.IO.FileSystem.AccessControl.dll", - "lib/netstandard1.3/System.IO.FileSystem.AccessControl.dll", - "lib/netstandard2.0/System.IO.FileSystem.AccessControl.dll", - "ref/net46/System.IO.FileSystem.AccessControl.dll", - "ref/net461/System.IO.FileSystem.AccessControl.dll", - "ref/net461/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/System.IO.FileSystem.AccessControl.dll", - "ref/netstandard1.3/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/de/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/es/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/fr/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/it/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/ja/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/ko/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/ru/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/zh-hans/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/zh-hant/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard2.0/System.IO.FileSystem.AccessControl.dll", - "ref/netstandard2.0/System.IO.FileSystem.AccessControl.xml", - "runtimes/win/lib/net46/System.IO.FileSystem.AccessControl.dll", - "runtimes/win/lib/net461/System.IO.FileSystem.AccessControl.dll", - "runtimes/win/lib/netstandard1.3/System.IO.FileSystem.AccessControl.dll", - "runtimes/win/lib/netstandard2.0/System.IO.FileSystem.AccessControl.dll", - "system.io.filesystem.accesscontrol.4.5.0.nupkg.sha512", - "system.io.filesystem.accesscontrol.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.IO.FileSystem.Primitives/4.3.0": { - "sha512": "WIWVPQlYLP/Zc9I6IakpBk1y8ryVGK83MtZx//zGKKi2hvHQWKAB7moRQCOz5Is/wNDksiYpocf3FeA3le6e5Q==", - "type": "package", - "path": "system.io.filesystem.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.IO.FileSystem.Primitives.dll", - "lib/netstandard1.3/System.IO.FileSystem.Primitives.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.IO.FileSystem.Primitives.dll", - "ref/netstandard1.3/System.IO.FileSystem.Primitives.dll", - "ref/netstandard1.3/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/de/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/es/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/fr/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/it/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/ja/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/ko/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/ru/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/zh-hans/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/zh-hant/System.IO.FileSystem.Primitives.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.filesystem.primitives.4.3.0.nupkg.sha512", - "system.io.filesystem.primitives.nuspec" - ] - }, - "System.Linq/4.3.0": { - "sha512": "6sx/4exSb0BfW6DmcfYW0OW+nBgo1UOp4vjGXfQJnWsupKn6LNrk80sXDcNxQvYOJn4TfKOfNQKB7XDS3GIEWA==", - "type": "package", - "path": "system.linq/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net463/System.Linq.dll", - "lib/netcore50/System.Linq.dll", - "lib/netstandard1.6/System.Linq.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net463/System.Linq.dll", - "ref/netcore50/System.Linq.dll", - "ref/netcore50/System.Linq.xml", - "ref/netcore50/de/System.Linq.xml", - "ref/netcore50/es/System.Linq.xml", - "ref/netcore50/fr/System.Linq.xml", - "ref/netcore50/it/System.Linq.xml", - "ref/netcore50/ja/System.Linq.xml", - "ref/netcore50/ko/System.Linq.xml", - "ref/netcore50/ru/System.Linq.xml", - "ref/netcore50/zh-hans/System.Linq.xml", - "ref/netcore50/zh-hant/System.Linq.xml", - "ref/netstandard1.0/System.Linq.dll", - "ref/netstandard1.0/System.Linq.xml", - "ref/netstandard1.0/de/System.Linq.xml", - "ref/netstandard1.0/es/System.Linq.xml", - "ref/netstandard1.0/fr/System.Linq.xml", - "ref/netstandard1.0/it/System.Linq.xml", - "ref/netstandard1.0/ja/System.Linq.xml", - "ref/netstandard1.0/ko/System.Linq.xml", - "ref/netstandard1.0/ru/System.Linq.xml", - "ref/netstandard1.0/zh-hans/System.Linq.xml", - "ref/netstandard1.0/zh-hant/System.Linq.xml", - "ref/netstandard1.6/System.Linq.dll", - "ref/netstandard1.6/System.Linq.xml", - "ref/netstandard1.6/de/System.Linq.xml", - "ref/netstandard1.6/es/System.Linq.xml", - "ref/netstandard1.6/fr/System.Linq.xml", - "ref/netstandard1.6/it/System.Linq.xml", - "ref/netstandard1.6/ja/System.Linq.xml", - "ref/netstandard1.6/ko/System.Linq.xml", - "ref/netstandard1.6/ru/System.Linq.xml", - "ref/netstandard1.6/zh-hans/System.Linq.xml", - "ref/netstandard1.6/zh-hant/System.Linq.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.linq.4.3.0.nupkg.sha512", - "system.linq.nuspec" - ] - }, - "System.Linq.Expressions/4.3.0": { - "sha512": "YbkO+a5vd5+8intkg+6PVEnN0FyBsFI19wRH5lanOyqrfDQXhLmZ91MjdHRKcuLDpc0TgA6iNBf6wyzPrlzebQ==", - "type": "package", - "path": "system.linq.expressions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net463/System.Linq.Expressions.dll", - "lib/netcore50/System.Linq.Expressions.dll", - "lib/netstandard1.6/System.Linq.Expressions.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net463/System.Linq.Expressions.dll", - "ref/netcore50/System.Linq.Expressions.dll", - "ref/netcore50/System.Linq.Expressions.xml", - "ref/netcore50/de/System.Linq.Expressions.xml", - "ref/netcore50/es/System.Linq.Expressions.xml", - "ref/netcore50/fr/System.Linq.Expressions.xml", - "ref/netcore50/it/System.Linq.Expressions.xml", - "ref/netcore50/ja/System.Linq.Expressions.xml", - "ref/netcore50/ko/System.Linq.Expressions.xml", - "ref/netcore50/ru/System.Linq.Expressions.xml", - "ref/netcore50/zh-hans/System.Linq.Expressions.xml", - "ref/netcore50/zh-hant/System.Linq.Expressions.xml", - "ref/netstandard1.0/System.Linq.Expressions.dll", - "ref/netstandard1.0/System.Linq.Expressions.xml", - "ref/netstandard1.0/de/System.Linq.Expressions.xml", - "ref/netstandard1.0/es/System.Linq.Expressions.xml", - "ref/netstandard1.0/fr/System.Linq.Expressions.xml", - "ref/netstandard1.0/it/System.Linq.Expressions.xml", - "ref/netstandard1.0/ja/System.Linq.Expressions.xml", - "ref/netstandard1.0/ko/System.Linq.Expressions.xml", - "ref/netstandard1.0/ru/System.Linq.Expressions.xml", - "ref/netstandard1.0/zh-hans/System.Linq.Expressions.xml", - "ref/netstandard1.0/zh-hant/System.Linq.Expressions.xml", - "ref/netstandard1.3/System.Linq.Expressions.dll", - "ref/netstandard1.3/System.Linq.Expressions.xml", - "ref/netstandard1.3/de/System.Linq.Expressions.xml", - "ref/netstandard1.3/es/System.Linq.Expressions.xml", - "ref/netstandard1.3/fr/System.Linq.Expressions.xml", - "ref/netstandard1.3/it/System.Linq.Expressions.xml", - "ref/netstandard1.3/ja/System.Linq.Expressions.xml", - "ref/netstandard1.3/ko/System.Linq.Expressions.xml", - "ref/netstandard1.3/ru/System.Linq.Expressions.xml", - "ref/netstandard1.3/zh-hans/System.Linq.Expressions.xml", - "ref/netstandard1.3/zh-hant/System.Linq.Expressions.xml", - "ref/netstandard1.6/System.Linq.Expressions.dll", - "ref/netstandard1.6/System.Linq.Expressions.xml", - "ref/netstandard1.6/de/System.Linq.Expressions.xml", - "ref/netstandard1.6/es/System.Linq.Expressions.xml", - "ref/netstandard1.6/fr/System.Linq.Expressions.xml", - "ref/netstandard1.6/it/System.Linq.Expressions.xml", - "ref/netstandard1.6/ja/System.Linq.Expressions.xml", - "ref/netstandard1.6/ko/System.Linq.Expressions.xml", - "ref/netstandard1.6/ru/System.Linq.Expressions.xml", - "ref/netstandard1.6/zh-hans/System.Linq.Expressions.xml", - "ref/netstandard1.6/zh-hant/System.Linq.Expressions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Linq.Expressions.dll", - "system.linq.expressions.4.3.0.nupkg.sha512", - "system.linq.expressions.nuspec" - ] - }, - "System.Linq.Queryable/4.3.0": { - "sha512": "In1Bmmvl/j52yPu3xgakQSI0YIckPUr870w4K5+Lak3JCCa8hl+my65lABOuKfYs4ugmZy25ScFerC4nz8+b6g==", - "type": "package", - "path": "system.linq.queryable/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/monoandroid10/_._", - "lib/monotouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Linq.Queryable.dll", - "lib/netstandard1.3/System.Linq.Queryable.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/monoandroid10/_._", - "ref/monotouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Linq.Queryable.dll", - "ref/netcore50/System.Linq.Queryable.xml", - "ref/netcore50/de/System.Linq.Queryable.xml", - "ref/netcore50/es/System.Linq.Queryable.xml", - "ref/netcore50/fr/System.Linq.Queryable.xml", - "ref/netcore50/it/System.Linq.Queryable.xml", - "ref/netcore50/ja/System.Linq.Queryable.xml", - "ref/netcore50/ko/System.Linq.Queryable.xml", - "ref/netcore50/ru/System.Linq.Queryable.xml", - "ref/netcore50/zh-hans/System.Linq.Queryable.xml", - "ref/netcore50/zh-hant/System.Linq.Queryable.xml", - "ref/netstandard1.0/System.Linq.Queryable.dll", - "ref/netstandard1.0/System.Linq.Queryable.xml", - "ref/netstandard1.0/de/System.Linq.Queryable.xml", - "ref/netstandard1.0/es/System.Linq.Queryable.xml", - "ref/netstandard1.0/fr/System.Linq.Queryable.xml", - "ref/netstandard1.0/it/System.Linq.Queryable.xml", - "ref/netstandard1.0/ja/System.Linq.Queryable.xml", - "ref/netstandard1.0/ko/System.Linq.Queryable.xml", - "ref/netstandard1.0/ru/System.Linq.Queryable.xml", - "ref/netstandard1.0/zh-hans/System.Linq.Queryable.xml", - "ref/netstandard1.0/zh-hant/System.Linq.Queryable.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.linq.queryable.4.3.0.nupkg.sha512", - "system.linq.queryable.nuspec" - ] - }, - "System.Memory/4.5.2": { - "sha512": "fvq1GNmUFwbKv+aLVYYdgu/+gc8Nu9oFujOxIjPrsf+meis9JBzTPDL6aP/eeGOz9yPj6rRLUbOjKMpsMEWpNg==", - "type": "package", - "path": "system.memory/4.5.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netcoreapp2.1/_._", - "lib/netstandard1.1/System.Memory.dll", - "lib/netstandard1.1/System.Memory.xml", - "lib/netstandard2.0/System.Memory.dll", - "lib/netstandard2.0/System.Memory.xml", - "ref/netcoreapp2.1/_._", - "system.memory.4.5.2.nupkg.sha512", - "system.memory.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Net.Http/4.3.0": { - "sha512": "6BBc6BUa7pWFL7KUI/c8wb18IobTZHTtcQKksxJI5F9DRDShdtOvBEJzg5jJbFdTll7gRE+5yXUlq72ciLE+QQ==", - "type": "package", - "path": "system.net.http/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/Xamarinmac20/_._", - "lib/monoandroid10/_._", - "lib/monotouch10/_._", - "lib/net45/_._", - "lib/net46/System.Net.Http.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/Xamarinmac20/_._", - "ref/monoandroid10/_._", - "ref/monotouch10/_._", - "ref/net45/_._", - "ref/net46/System.Net.Http.dll", - "ref/net46/System.Net.Http.xml", - "ref/net46/de/System.Net.Http.xml", - "ref/net46/es/System.Net.Http.xml", - "ref/net46/fr/System.Net.Http.xml", - "ref/net46/it/System.Net.Http.xml", - "ref/net46/ja/System.Net.Http.xml", - "ref/net46/ko/System.Net.Http.xml", - "ref/net46/ru/System.Net.Http.xml", - "ref/net46/zh-hans/System.Net.Http.xml", - "ref/net46/zh-hant/System.Net.Http.xml", - "ref/netcore50/System.Net.Http.dll", - "ref/netcore50/System.Net.Http.xml", - "ref/netcore50/de/System.Net.Http.xml", - "ref/netcore50/es/System.Net.Http.xml", - "ref/netcore50/fr/System.Net.Http.xml", - "ref/netcore50/it/System.Net.Http.xml", - "ref/netcore50/ja/System.Net.Http.xml", - "ref/netcore50/ko/System.Net.Http.xml", - "ref/netcore50/ru/System.Net.Http.xml", - "ref/netcore50/zh-hans/System.Net.Http.xml", - "ref/netcore50/zh-hant/System.Net.Http.xml", - "ref/netstandard1.1/System.Net.Http.dll", - "ref/netstandard1.1/System.Net.Http.xml", - "ref/netstandard1.1/de/System.Net.Http.xml", - "ref/netstandard1.1/es/System.Net.Http.xml", - "ref/netstandard1.1/fr/System.Net.Http.xml", - "ref/netstandard1.1/it/System.Net.Http.xml", - "ref/netstandard1.1/ja/System.Net.Http.xml", - "ref/netstandard1.1/ko/System.Net.Http.xml", - "ref/netstandard1.1/ru/System.Net.Http.xml", - "ref/netstandard1.1/zh-hans/System.Net.Http.xml", - "ref/netstandard1.1/zh-hant/System.Net.Http.xml", - "ref/netstandard1.3/System.Net.Http.dll", - "ref/netstandard1.3/System.Net.Http.xml", - "ref/netstandard1.3/de/System.Net.Http.xml", - "ref/netstandard1.3/es/System.Net.Http.xml", - "ref/netstandard1.3/fr/System.Net.Http.xml", - "ref/netstandard1.3/it/System.Net.Http.xml", - "ref/netstandard1.3/ja/System.Net.Http.xml", - "ref/netstandard1.3/ko/System.Net.Http.xml", - "ref/netstandard1.3/ru/System.Net.Http.xml", - "ref/netstandard1.3/zh-hans/System.Net.Http.xml", - "ref/netstandard1.3/zh-hant/System.Net.Http.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.6/System.Net.Http.dll", - "runtimes/win/lib/net46/System.Net.Http.dll", - "runtimes/win/lib/netcore50/System.Net.Http.dll", - "runtimes/win/lib/netstandard1.3/System.Net.Http.dll", - "system.net.http.4.3.0.nupkg.sha512", - "system.net.http.nuspec" - ] - }, - "System.Net.NameResolution/4.3.0": { - "sha512": "QNOeEx/no5Lljp9YtRa124g4PekcBbdx9eUJrPRsyHQnHpBiPTJ6sDkAOrjycUFEaUOQJhJ43jJOGu4iioKKtA==", - "type": "package", - "path": "system.net.nameresolution/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Net.NameResolution.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Net.NameResolution.dll", - "ref/netstandard1.3/System.Net.NameResolution.dll", - "ref/netstandard1.3/System.Net.NameResolution.xml", - "ref/netstandard1.3/de/System.Net.NameResolution.xml", - "ref/netstandard1.3/es/System.Net.NameResolution.xml", - "ref/netstandard1.3/fr/System.Net.NameResolution.xml", - "ref/netstandard1.3/it/System.Net.NameResolution.xml", - "ref/netstandard1.3/ja/System.Net.NameResolution.xml", - "ref/netstandard1.3/ko/System.Net.NameResolution.xml", - "ref/netstandard1.3/ru/System.Net.NameResolution.xml", - "ref/netstandard1.3/zh-hans/System.Net.NameResolution.xml", - "ref/netstandard1.3/zh-hant/System.Net.NameResolution.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Net.NameResolution.dll", - "runtimes/win/lib/net46/System.Net.NameResolution.dll", - "runtimes/win/lib/netcore50/System.Net.NameResolution.dll", - "runtimes/win/lib/netstandard1.3/System.Net.NameResolution.dll", - "system.net.nameresolution.4.3.0.nupkg.sha512", - "system.net.nameresolution.nuspec" - ] - }, - "System.Net.NetworkInformation/4.3.0": { - "sha512": "zNVmWVry0pAu7lcrRBhwwU96WUdbsrGL3azyzsbXmVNptae1+Za+UgOe9Z6s8iaWhPn7/l4wQqhC56HZWq7tkg==", - "type": "package", - "path": "system.net.networkinformation/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.Net.NetworkInformation.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.Net.NetworkInformation.dll", - "ref/netcore50/System.Net.NetworkInformation.dll", - "ref/netcore50/System.Net.NetworkInformation.xml", - "ref/netcore50/de/System.Net.NetworkInformation.xml", - "ref/netcore50/es/System.Net.NetworkInformation.xml", - "ref/netcore50/fr/System.Net.NetworkInformation.xml", - "ref/netcore50/it/System.Net.NetworkInformation.xml", - "ref/netcore50/ja/System.Net.NetworkInformation.xml", - "ref/netcore50/ko/System.Net.NetworkInformation.xml", - "ref/netcore50/ru/System.Net.NetworkInformation.xml", - "ref/netcore50/zh-hans/System.Net.NetworkInformation.xml", - "ref/netcore50/zh-hant/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/System.Net.NetworkInformation.dll", - "ref/netstandard1.0/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/de/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/es/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/fr/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/it/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/ja/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/ko/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/ru/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/zh-hans/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/zh-hant/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/System.Net.NetworkInformation.dll", - "ref/netstandard1.3/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/de/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/es/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/fr/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/it/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/ja/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/ko/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/ru/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/zh-hans/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/zh-hant/System.Net.NetworkInformation.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/linux/lib/netstandard1.3/System.Net.NetworkInformation.dll", - "runtimes/osx/lib/netstandard1.3/System.Net.NetworkInformation.dll", - "runtimes/win/lib/net46/System.Net.NetworkInformation.dll", - "runtimes/win/lib/netcore50/System.Net.NetworkInformation.dll", - "runtimes/win/lib/netstandard1.3/System.Net.NetworkInformation.dll", - "system.net.networkinformation.4.3.0.nupkg.sha512", - "system.net.networkinformation.nuspec" - ] - }, - "System.Net.Primitives/4.3.0": { - "sha512": "n3/ezjMKgfMxLqfIBJJ4UkE77iySnzBmtzaZOAPfR8wGkvvKI2wiK/GdyPWbQvVPKkwA2ppNYk5FjaWHTRJ85g==", - "type": "package", - "path": "system.net.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Net.Primitives.dll", - "ref/netcore50/System.Net.Primitives.xml", - "ref/netcore50/de/System.Net.Primitives.xml", - "ref/netcore50/es/System.Net.Primitives.xml", - "ref/netcore50/fr/System.Net.Primitives.xml", - "ref/netcore50/it/System.Net.Primitives.xml", - "ref/netcore50/ja/System.Net.Primitives.xml", - "ref/netcore50/ko/System.Net.Primitives.xml", - "ref/netcore50/ru/System.Net.Primitives.xml", - "ref/netcore50/zh-hans/System.Net.Primitives.xml", - "ref/netcore50/zh-hant/System.Net.Primitives.xml", - "ref/netstandard1.0/System.Net.Primitives.dll", - "ref/netstandard1.0/System.Net.Primitives.xml", - "ref/netstandard1.0/de/System.Net.Primitives.xml", - "ref/netstandard1.0/es/System.Net.Primitives.xml", - "ref/netstandard1.0/fr/System.Net.Primitives.xml", - "ref/netstandard1.0/it/System.Net.Primitives.xml", - "ref/netstandard1.0/ja/System.Net.Primitives.xml", - "ref/netstandard1.0/ko/System.Net.Primitives.xml", - "ref/netstandard1.0/ru/System.Net.Primitives.xml", - "ref/netstandard1.0/zh-hans/System.Net.Primitives.xml", - "ref/netstandard1.0/zh-hant/System.Net.Primitives.xml", - "ref/netstandard1.1/System.Net.Primitives.dll", - "ref/netstandard1.1/System.Net.Primitives.xml", - "ref/netstandard1.1/de/System.Net.Primitives.xml", - "ref/netstandard1.1/es/System.Net.Primitives.xml", - "ref/netstandard1.1/fr/System.Net.Primitives.xml", - "ref/netstandard1.1/it/System.Net.Primitives.xml", - "ref/netstandard1.1/ja/System.Net.Primitives.xml", - "ref/netstandard1.1/ko/System.Net.Primitives.xml", - "ref/netstandard1.1/ru/System.Net.Primitives.xml", - "ref/netstandard1.1/zh-hans/System.Net.Primitives.xml", - "ref/netstandard1.1/zh-hant/System.Net.Primitives.xml", - "ref/netstandard1.3/System.Net.Primitives.dll", - "ref/netstandard1.3/System.Net.Primitives.xml", - "ref/netstandard1.3/de/System.Net.Primitives.xml", - "ref/netstandard1.3/es/System.Net.Primitives.xml", - "ref/netstandard1.3/fr/System.Net.Primitives.xml", - "ref/netstandard1.3/it/System.Net.Primitives.xml", - "ref/netstandard1.3/ja/System.Net.Primitives.xml", - "ref/netstandard1.3/ko/System.Net.Primitives.xml", - "ref/netstandard1.3/ru/System.Net.Primitives.xml", - "ref/netstandard1.3/zh-hans/System.Net.Primitives.xml", - "ref/netstandard1.3/zh-hant/System.Net.Primitives.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.net.primitives.4.3.0.nupkg.sha512", - "system.net.primitives.nuspec" - ] - }, - "System.Net.Security/4.3.2": { - "sha512": "xT2jbYpbBo3ha87rViHoTA6WdvqOAW37drmqyx/6LD8p7HEPT2qgdxoimRzWtPg8Jh4X5G9BV2seeTv4x6FYlA==", - "type": "package", - "path": "system.net.security/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Net.Security.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Net.Security.dll", - "ref/netstandard1.3/System.Net.Security.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.6/System.Net.Security.dll", - "runtimes/win/lib/net46/System.Net.Security.dll", - "runtimes/win/lib/netstandard1.3/System.Net.Security.dll", - "runtimes/win7/lib/netcore50/_._", - "system.net.security.4.3.2.nupkg.sha512", - "system.net.security.nuspec" - ] - }, - "System.Net.Sockets/4.3.0": { - "sha512": "4y7ZUY6WMOme3PGWPD0OcEfqglKFPJJg61QDpCBhcK4o/SfrI5852k0tt2b4MLPr3J5NouaXviAkHZKAgiAJVQ==", - "type": "package", - "path": "system.net.sockets/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Net.Sockets.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Net.Sockets.dll", - "ref/netstandard1.3/System.Net.Sockets.dll", - "ref/netstandard1.3/System.Net.Sockets.xml", - "ref/netstandard1.3/de/System.Net.Sockets.xml", - "ref/netstandard1.3/es/System.Net.Sockets.xml", - "ref/netstandard1.3/fr/System.Net.Sockets.xml", - "ref/netstandard1.3/it/System.Net.Sockets.xml", - "ref/netstandard1.3/ja/System.Net.Sockets.xml", - "ref/netstandard1.3/ko/System.Net.Sockets.xml", - "ref/netstandard1.3/ru/System.Net.Sockets.xml", - "ref/netstandard1.3/zh-hans/System.Net.Sockets.xml", - "ref/netstandard1.3/zh-hant/System.Net.Sockets.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.net.sockets.4.3.0.nupkg.sha512", - "system.net.sockets.nuspec" - ] - }, - "System.ObjectModel/4.3.0": { - "sha512": "QJvKPSE5vR0APHEUALotteV2u1TVk6pUHsNXbnsgKbYBWascWyxOc4kmexuV682MLwZNxuH1Pmk6rLFzfwZhIw==", - "type": "package", - "path": "system.objectmodel/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.ObjectModel.dll", - "lib/netstandard1.3/System.ObjectModel.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.ObjectModel.dll", - "ref/netcore50/System.ObjectModel.xml", - "ref/netcore50/de/System.ObjectModel.xml", - "ref/netcore50/es/System.ObjectModel.xml", - "ref/netcore50/fr/System.ObjectModel.xml", - "ref/netcore50/it/System.ObjectModel.xml", - "ref/netcore50/ja/System.ObjectModel.xml", - "ref/netcore50/ko/System.ObjectModel.xml", - "ref/netcore50/ru/System.ObjectModel.xml", - "ref/netcore50/zh-hans/System.ObjectModel.xml", - "ref/netcore50/zh-hant/System.ObjectModel.xml", - "ref/netstandard1.0/System.ObjectModel.dll", - "ref/netstandard1.0/System.ObjectModel.xml", - "ref/netstandard1.0/de/System.ObjectModel.xml", - "ref/netstandard1.0/es/System.ObjectModel.xml", - "ref/netstandard1.0/fr/System.ObjectModel.xml", - "ref/netstandard1.0/it/System.ObjectModel.xml", - "ref/netstandard1.0/ja/System.ObjectModel.xml", - "ref/netstandard1.0/ko/System.ObjectModel.xml", - "ref/netstandard1.0/ru/System.ObjectModel.xml", - "ref/netstandard1.0/zh-hans/System.ObjectModel.xml", - "ref/netstandard1.0/zh-hant/System.ObjectModel.xml", - "ref/netstandard1.3/System.ObjectModel.dll", - "ref/netstandard1.3/System.ObjectModel.xml", - "ref/netstandard1.3/de/System.ObjectModel.xml", - "ref/netstandard1.3/es/System.ObjectModel.xml", - "ref/netstandard1.3/fr/System.ObjectModel.xml", - "ref/netstandard1.3/it/System.ObjectModel.xml", - "ref/netstandard1.3/ja/System.ObjectModel.xml", - "ref/netstandard1.3/ko/System.ObjectModel.xml", - "ref/netstandard1.3/ru/System.ObjectModel.xml", - "ref/netstandard1.3/zh-hans/System.ObjectModel.xml", - "ref/netstandard1.3/zh-hant/System.ObjectModel.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.objectmodel.4.3.0.nupkg.sha512", - "system.objectmodel.nuspec" - ] - }, - "System.Private.ServiceModel/4.5.3": { - "sha512": "ancrQgJagx+yC4SZbuE+eShiEAUIF0E1d21TRSoy1C/rTwafAVcBr/fKibkq5TQzyy9uNil2tx2/iaUxsy0S9g==", - "type": "package", - "path": "system.private.servicemodel/4.5.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "ref/netstandard/_._", - "runtimes/unix/lib/netstandard1.3/System.Private.ServiceModel.dll", - "runtimes/unix/lib/netstandard2.0/System.Private.ServiceModel.dll", - "runtimes/win/lib/netcore50/System.Private.ServiceModel.dll", - "runtimes/win/lib/netstandard1.3/System.Private.ServiceModel.dll", - "runtimes/win/lib/netstandard2.0/System.Private.ServiceModel.dll", - "runtimes/win/lib/uap10.0.16300/System.Private.ServiceModel.dll", - "system.private.servicemodel.4.5.3.nupkg.sha512", - "system.private.servicemodel.nuspec", - "version.txt" - ] - }, - "System.Reactive/4.1.6": { - "sha512": "vCBMgMa1J4ZKvyJdnS6rl3BWiH5UZ6qLu2NyjxzBxIh1+IyQ/aPUs8A7HbZBCaQnoc7Y95+dzV3+PI+WxeQbpg==", - "type": "package", - "path": "system.reactive/4.1.6", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net46/System.Reactive.dll", - "lib/net46/System.Reactive.xml", - "lib/netstandard2.0/System.Reactive.dll", - "lib/netstandard2.0/System.Reactive.xml", - "lib/uap10.0.16299/System.Reactive.dll", - "lib/uap10.0.16299/System.Reactive.pri", - "lib/uap10.0.16299/System.Reactive.xml", - "lib/uap10.0/System.Reactive.dll", - "lib/uap10.0/System.Reactive.pri", - "lib/uap10.0/System.Reactive.xml", - "system.reactive.4.1.6.nupkg.sha512", - "system.reactive.nuspec" - ] - }, - "System.Reflection/4.3.0": { - "sha512": "IyW2ftYNzgMCgHBk8lQiy+G3+ydbU5tE+6PEqM5JJvIdeFKaXDSzHAPYDREPe6zpr5WJ1Fcma+rAFCIAV6+DMw==", - "type": "package", - "path": "system.reflection/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Reflection.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Reflection.dll", - "ref/netcore50/System.Reflection.dll", - "ref/netcore50/System.Reflection.xml", - "ref/netcore50/de/System.Reflection.xml", - "ref/netcore50/es/System.Reflection.xml", - "ref/netcore50/fr/System.Reflection.xml", - "ref/netcore50/it/System.Reflection.xml", - "ref/netcore50/ja/System.Reflection.xml", - "ref/netcore50/ko/System.Reflection.xml", - "ref/netcore50/ru/System.Reflection.xml", - "ref/netcore50/zh-hans/System.Reflection.xml", - "ref/netcore50/zh-hant/System.Reflection.xml", - "ref/netstandard1.0/System.Reflection.dll", - "ref/netstandard1.0/System.Reflection.xml", - "ref/netstandard1.0/de/System.Reflection.xml", - "ref/netstandard1.0/es/System.Reflection.xml", - "ref/netstandard1.0/fr/System.Reflection.xml", - "ref/netstandard1.0/it/System.Reflection.xml", - "ref/netstandard1.0/ja/System.Reflection.xml", - "ref/netstandard1.0/ko/System.Reflection.xml", - "ref/netstandard1.0/ru/System.Reflection.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.xml", - "ref/netstandard1.3/System.Reflection.dll", - "ref/netstandard1.3/System.Reflection.xml", - "ref/netstandard1.3/de/System.Reflection.xml", - "ref/netstandard1.3/es/System.Reflection.xml", - "ref/netstandard1.3/fr/System.Reflection.xml", - "ref/netstandard1.3/it/System.Reflection.xml", - "ref/netstandard1.3/ja/System.Reflection.xml", - "ref/netstandard1.3/ko/System.Reflection.xml", - "ref/netstandard1.3/ru/System.Reflection.xml", - "ref/netstandard1.3/zh-hans/System.Reflection.xml", - "ref/netstandard1.3/zh-hant/System.Reflection.xml", - "ref/netstandard1.5/System.Reflection.dll", - "ref/netstandard1.5/System.Reflection.xml", - "ref/netstandard1.5/de/System.Reflection.xml", - "ref/netstandard1.5/es/System.Reflection.xml", - "ref/netstandard1.5/fr/System.Reflection.xml", - "ref/netstandard1.5/it/System.Reflection.xml", - "ref/netstandard1.5/ja/System.Reflection.xml", - "ref/netstandard1.5/ko/System.Reflection.xml", - "ref/netstandard1.5/ru/System.Reflection.xml", - "ref/netstandard1.5/zh-hans/System.Reflection.xml", - "ref/netstandard1.5/zh-hant/System.Reflection.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.reflection.4.3.0.nupkg.sha512", - "system.reflection.nuspec" - ] - }, - "System.Reflection.DispatchProxy/4.5.0": { - "sha512": "+UW1hq11TNSeb+16rIk8hRQ02o339NFyzMc4ma/FqmxBzM30l1c2IherBB4ld1MNcenS48fz8tbt50OW4rVULA==", - "type": "package", - "path": "system.reflection.dispatchproxy/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net461/System.Reflection.DispatchProxy.dll", - "lib/netcoreapp2.0/System.Reflection.DispatchProxy.dll", - "lib/netstandard1.3/System.Reflection.DispatchProxy.dll", - "lib/netstandard2.0/System.Reflection.DispatchProxy.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/netstandard1.3/System.Reflection.DispatchProxy.dll", - "ref/netstandard1.3/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/de/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/es/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/fr/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/it/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/ja/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/ko/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/ru/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/zh-hans/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/zh-hant/System.Reflection.DispatchProxy.xml", - "ref/netstandard2.0/System.Reflection.DispatchProxy.dll", - "ref/netstandard2.0/System.Reflection.DispatchProxy.xml", - "ref/uap10.0.16299/System.Reflection.DispatchProxy.dll", - "ref/uap10.0.16299/System.Reflection.DispatchProxy.xml", - "ref/uap10.0.16300/System.Reflection.DispatchProxy.dll", - "ref/uap10.0.16300/System.Reflection.DispatchProxy.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Reflection.DispatchProxy.dll", - "runtimes/win-aot/lib/uap10.0.16299/System.Reflection.DispatchProxy.dll", - "runtimes/win/lib/uap10.0.16299/System.Reflection.DispatchProxy.dll", - "system.reflection.dispatchproxy.4.5.0.nupkg.sha512", - "system.reflection.dispatchproxy.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Reflection.Emit/4.3.0": { - "sha512": "vkUFFGejarllQQ8RKkdfuBUQpVlTR9HMDEawKOBDajOSGN08Bz8EjC0zi2fcE7RXQikLbEb1WYJQP3So8mmIGA==", - "type": "package", - "path": "system.reflection.emit/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/monotouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Reflection.Emit.dll", - "lib/netstandard1.3/System.Reflection.Emit.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/net45/_._", - "ref/netstandard1.1/System.Reflection.Emit.dll", - "ref/netstandard1.1/System.Reflection.Emit.xml", - "ref/netstandard1.1/de/System.Reflection.Emit.xml", - "ref/netstandard1.1/es/System.Reflection.Emit.xml", - "ref/netstandard1.1/fr/System.Reflection.Emit.xml", - "ref/netstandard1.1/it/System.Reflection.Emit.xml", - "ref/netstandard1.1/ja/System.Reflection.Emit.xml", - "ref/netstandard1.1/ko/System.Reflection.Emit.xml", - "ref/netstandard1.1/ru/System.Reflection.Emit.xml", - "ref/netstandard1.1/zh-hans/System.Reflection.Emit.xml", - "ref/netstandard1.1/zh-hant/System.Reflection.Emit.xml", - "ref/xamarinmac20/_._", - "system.reflection.emit.4.3.0.nupkg.sha512", - "system.reflection.emit.nuspec" - ] - }, - "System.Reflection.Emit.ILGeneration/4.3.0": { - "sha512": "6b5fYr9ksZR6SYVzNzBqXQmAaGtY1mWYnpQAarBKp+C79NhUPRtX1bs4B5BS8nXzObcwVKc1fk+jVyCKCshdaQ==", - "type": "package", - "path": "system.reflection.emit.ilgeneration/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Reflection.Emit.ILGeneration.dll", - "lib/netstandard1.3/System.Reflection.Emit.ILGeneration.dll", - "lib/portable-net45+wp8/_._", - "lib/wp80/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netstandard1.0/System.Reflection.Emit.ILGeneration.dll", - "ref/netstandard1.0/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/de/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/es/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/fr/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/it/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/ja/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/ko/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/ru/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Emit.ILGeneration.xml", - "ref/portable-net45+wp8/_._", - "ref/wp80/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/_._", - "system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512", - "system.reflection.emit.ilgeneration.nuspec" - ] - }, - "System.Reflection.Emit.Lightweight/4.3.0": { - "sha512": "rVivBylr0ISQegifkgJvo4mLdk651qB8lBS1UKg6xgRW8yo0Enwpu5OpYz+we6n9go97QaMdzl/wGafPGrKUNQ==", - "type": "package", - "path": "system.reflection.emit.lightweight/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Reflection.Emit.Lightweight.dll", - "lib/netstandard1.3/System.Reflection.Emit.Lightweight.dll", - "lib/portable-net45+wp8/_._", - "lib/wp80/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netstandard1.0/System.Reflection.Emit.Lightweight.dll", - "ref/netstandard1.0/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/de/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/es/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/fr/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/it/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/ja/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/ko/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/ru/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Emit.Lightweight.xml", - "ref/portable-net45+wp8/_._", - "ref/wp80/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/_._", - "system.reflection.emit.lightweight.4.3.0.nupkg.sha512", - "system.reflection.emit.lightweight.nuspec" - ] - }, - "System.Reflection.Extensions/4.3.0": { - "sha512": "Bs/ZksjX/Zq2QyqwK+mBoBtlWChabiangloGTU78zgjZ5zRPA/oZsDOiRZ1CsLgOjBQAzjm0ehdShpq4glsEdQ==", - "type": "package", - "path": "system.reflection.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Reflection.Extensions.dll", - "ref/netcore50/System.Reflection.Extensions.xml", - "ref/netcore50/de/System.Reflection.Extensions.xml", - "ref/netcore50/es/System.Reflection.Extensions.xml", - "ref/netcore50/fr/System.Reflection.Extensions.xml", - "ref/netcore50/it/System.Reflection.Extensions.xml", - "ref/netcore50/ja/System.Reflection.Extensions.xml", - "ref/netcore50/ko/System.Reflection.Extensions.xml", - "ref/netcore50/ru/System.Reflection.Extensions.xml", - "ref/netcore50/zh-hans/System.Reflection.Extensions.xml", - "ref/netcore50/zh-hant/System.Reflection.Extensions.xml", - "ref/netstandard1.0/System.Reflection.Extensions.dll", - "ref/netstandard1.0/System.Reflection.Extensions.xml", - "ref/netstandard1.0/de/System.Reflection.Extensions.xml", - "ref/netstandard1.0/es/System.Reflection.Extensions.xml", - "ref/netstandard1.0/fr/System.Reflection.Extensions.xml", - "ref/netstandard1.0/it/System.Reflection.Extensions.xml", - "ref/netstandard1.0/ja/System.Reflection.Extensions.xml", - "ref/netstandard1.0/ko/System.Reflection.Extensions.xml", - "ref/netstandard1.0/ru/System.Reflection.Extensions.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Extensions.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Extensions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.reflection.extensions.4.3.0.nupkg.sha512", - "system.reflection.extensions.nuspec" - ] - }, - "System.Reflection.Metadata/1.4.1": { - "sha512": "yZMnG+4FlPfxGngBmuLAL38hGY+WSQD8aCEWkCNRrLlgD9CRw7t/M9Zuru1A7Gy0pJJTp9vXuyX/9N6NQwNapQ==", - "type": "package", - "path": "system.reflection.metadata/1.4.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.1/System.Reflection.Metadata.dll", - "lib/netstandard1.1/System.Reflection.Metadata.xml", - "lib/portable-net45+win8/System.Reflection.Metadata.dll", - "lib/portable-net45+win8/System.Reflection.Metadata.xml", - "system.reflection.metadata.1.4.1.nupkg.sha512", - "system.reflection.metadata.nuspec" - ] - }, - "System.Reflection.Primitives/4.3.0": { - "sha512": "1LnMkF9aXKuQAgYzjoiQaL9mwY7oY6KdaO/zzeLMynNBEqKoUfLi5TiKIewoAF+hkxfGTZsjkjsF1jRL4uSeqg==", - "type": "package", - "path": "system.reflection.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Reflection.Primitives.dll", - "ref/netcore50/System.Reflection.Primitives.xml", - "ref/netcore50/de/System.Reflection.Primitives.xml", - "ref/netcore50/es/System.Reflection.Primitives.xml", - "ref/netcore50/fr/System.Reflection.Primitives.xml", - "ref/netcore50/it/System.Reflection.Primitives.xml", - "ref/netcore50/ja/System.Reflection.Primitives.xml", - "ref/netcore50/ko/System.Reflection.Primitives.xml", - "ref/netcore50/ru/System.Reflection.Primitives.xml", - "ref/netcore50/zh-hans/System.Reflection.Primitives.xml", - "ref/netcore50/zh-hant/System.Reflection.Primitives.xml", - "ref/netstandard1.0/System.Reflection.Primitives.dll", - "ref/netstandard1.0/System.Reflection.Primitives.xml", - "ref/netstandard1.0/de/System.Reflection.Primitives.xml", - "ref/netstandard1.0/es/System.Reflection.Primitives.xml", - "ref/netstandard1.0/fr/System.Reflection.Primitives.xml", - "ref/netstandard1.0/it/System.Reflection.Primitives.xml", - "ref/netstandard1.0/ja/System.Reflection.Primitives.xml", - "ref/netstandard1.0/ko/System.Reflection.Primitives.xml", - "ref/netstandard1.0/ru/System.Reflection.Primitives.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Primitives.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Primitives.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.reflection.primitives.4.3.0.nupkg.sha512", - "system.reflection.primitives.nuspec" - ] - }, - "System.Reflection.TypeExtensions/4.5.1": { - "sha512": "fO8GMEkgoKioJ7cglZbhcnBgkCWWn9poS3G2jevS+fuwW9xJXMx7/1kr7dkwOJfo0pWqxLFWVcxlOr+WeK5ipA==", - "type": "package", - "path": "system.reflection.typeextensions/4.5.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Reflection.TypeExtensions.dll", - "lib/net461/System.Reflection.TypeExtensions.dll", - "lib/netcore50/System.Reflection.TypeExtensions.dll", - "lib/netcoreapp1.0/System.Reflection.TypeExtensions.dll", - "lib/netcoreapp2.0/_._", - "lib/netstandard1.3/System.Reflection.TypeExtensions.dll", - "lib/netstandard1.5/System.Reflection.TypeExtensions.dll", - "lib/netstandard2.0/System.Reflection.TypeExtensions.dll", - "lib/uap10.0.16299/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Reflection.TypeExtensions.dll", - "ref/net461/System.Reflection.TypeExtensions.dll", - "ref/net461/System.Reflection.TypeExtensions.xml", - "ref/netcoreapp2.0/_._", - "ref/netstandard1.3/System.Reflection.TypeExtensions.dll", - "ref/netstandard1.3/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/de/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/es/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/fr/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/it/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/ja/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/ko/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/ru/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/zh-hans/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/zh-hant/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/System.Reflection.TypeExtensions.dll", - "ref/netstandard1.5/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/de/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/es/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/fr/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/it/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/ja/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/ko/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/ru/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/zh-hans/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/zh-hant/System.Reflection.TypeExtensions.xml", - "ref/netstandard2.0/System.Reflection.TypeExtensions.dll", - "ref/netstandard2.0/System.Reflection.TypeExtensions.xml", - "ref/uap10.0.16299/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Reflection.TypeExtensions.dll", - "runtimes/aot/lib/uap10.0.16299/_._", - "system.reflection.typeextensions.4.5.1.nupkg.sha512", - "system.reflection.typeextensions.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Resources.ResourceManager/4.3.0": { - "sha512": "kGfbKPHEjQj8Uq1Apgj4jBStkRJkZ0Hdr0Jv3+aL7WGrAZVLF5Rh5h0Yc3FgDB5uXDbHiJk/WhBaZPVwKmuB1A==", - "type": "package", - "path": "system.resources.resourcemanager/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Resources.ResourceManager.dll", - "ref/netcore50/System.Resources.ResourceManager.xml", - "ref/netcore50/de/System.Resources.ResourceManager.xml", - "ref/netcore50/es/System.Resources.ResourceManager.xml", - "ref/netcore50/fr/System.Resources.ResourceManager.xml", - "ref/netcore50/it/System.Resources.ResourceManager.xml", - "ref/netcore50/ja/System.Resources.ResourceManager.xml", - "ref/netcore50/ko/System.Resources.ResourceManager.xml", - "ref/netcore50/ru/System.Resources.ResourceManager.xml", - "ref/netcore50/zh-hans/System.Resources.ResourceManager.xml", - "ref/netcore50/zh-hant/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/System.Resources.ResourceManager.dll", - "ref/netstandard1.0/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/de/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/es/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/fr/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/it/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/ja/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/ko/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/ru/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/zh-hans/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/zh-hant/System.Resources.ResourceManager.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.resources.resourcemanager.4.3.0.nupkg.sha512", - "system.resources.resourcemanager.nuspec" - ] - }, - "System.Runtime/4.3.0": { - "sha512": "kqsiSfCAc8+v3Ez719s21lGthxuNi6lhAGmCGH3jdL9KMK+T8V9zsFrzQ/enDL1ISwTWRlcFh2Nq5yFx6wcU+w==", - "type": "package", - "path": "system.runtime/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Runtime.dll", - "lib/portable-net45+win8+wp80+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Runtime.dll", - "ref/netcore50/System.Runtime.dll", - "ref/netcore50/System.Runtime.xml", - "ref/netcore50/de/System.Runtime.xml", - "ref/netcore50/es/System.Runtime.xml", - "ref/netcore50/fr/System.Runtime.xml", - "ref/netcore50/it/System.Runtime.xml", - "ref/netcore50/ja/System.Runtime.xml", - "ref/netcore50/ko/System.Runtime.xml", - "ref/netcore50/ru/System.Runtime.xml", - "ref/netcore50/zh-hans/System.Runtime.xml", - "ref/netcore50/zh-hant/System.Runtime.xml", - "ref/netstandard1.0/System.Runtime.dll", - "ref/netstandard1.0/System.Runtime.xml", - "ref/netstandard1.0/de/System.Runtime.xml", - "ref/netstandard1.0/es/System.Runtime.xml", - "ref/netstandard1.0/fr/System.Runtime.xml", - "ref/netstandard1.0/it/System.Runtime.xml", - "ref/netstandard1.0/ja/System.Runtime.xml", - "ref/netstandard1.0/ko/System.Runtime.xml", - "ref/netstandard1.0/ru/System.Runtime.xml", - "ref/netstandard1.0/zh-hans/System.Runtime.xml", - "ref/netstandard1.0/zh-hant/System.Runtime.xml", - "ref/netstandard1.2/System.Runtime.dll", - "ref/netstandard1.2/System.Runtime.xml", - "ref/netstandard1.2/de/System.Runtime.xml", - "ref/netstandard1.2/es/System.Runtime.xml", - "ref/netstandard1.2/fr/System.Runtime.xml", - "ref/netstandard1.2/it/System.Runtime.xml", - "ref/netstandard1.2/ja/System.Runtime.xml", - "ref/netstandard1.2/ko/System.Runtime.xml", - "ref/netstandard1.2/ru/System.Runtime.xml", - "ref/netstandard1.2/zh-hans/System.Runtime.xml", - "ref/netstandard1.2/zh-hant/System.Runtime.xml", - "ref/netstandard1.3/System.Runtime.dll", - "ref/netstandard1.3/System.Runtime.xml", - "ref/netstandard1.3/de/System.Runtime.xml", - "ref/netstandard1.3/es/System.Runtime.xml", - "ref/netstandard1.3/fr/System.Runtime.xml", - "ref/netstandard1.3/it/System.Runtime.xml", - "ref/netstandard1.3/ja/System.Runtime.xml", - "ref/netstandard1.3/ko/System.Runtime.xml", - "ref/netstandard1.3/ru/System.Runtime.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.xml", - "ref/netstandard1.5/System.Runtime.dll", - "ref/netstandard1.5/System.Runtime.xml", - "ref/netstandard1.5/de/System.Runtime.xml", - "ref/netstandard1.5/es/System.Runtime.xml", - "ref/netstandard1.5/fr/System.Runtime.xml", - "ref/netstandard1.5/it/System.Runtime.xml", - "ref/netstandard1.5/ja/System.Runtime.xml", - "ref/netstandard1.5/ko/System.Runtime.xml", - "ref/netstandard1.5/ru/System.Runtime.xml", - "ref/netstandard1.5/zh-hans/System.Runtime.xml", - "ref/netstandard1.5/zh-hant/System.Runtime.xml", - "ref/portable-net45+win8+wp80+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.4.3.0.nupkg.sha512", - "system.runtime.nuspec" - ] - }, - "System.Runtime.CompilerServices.Unsafe/4.5.2": { - "sha512": "wprSFgext8cwqymChhrBLu62LMg/1u92bU+VOwyfBimSPVFXtsNqEWC92Pf9ofzJFlk4IHmJA75EDJn1b2goAQ==", - "type": "package", - "path": "system.runtime.compilerservices.unsafe/4.5.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll", - "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.xml", - "lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll", - "lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml", - "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll", - "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml", - "ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll", - "ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml", - "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll", - "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml", - "system.runtime.compilerservices.unsafe.4.5.2.nupkg.sha512", - "system.runtime.compilerservices.unsafe.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Runtime.Extensions/4.3.0": { - "sha512": "aAoysZwr1QJvhoeqU4KupPQytPAy+L3imfrLYYxW1XNpre9/fMjmCtgq48EuXdUHckkTY7+ARMd4d4YopmBbvA==", - "type": "package", - "path": "system.runtime.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Runtime.Extensions.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Runtime.Extensions.dll", - "ref/netcore50/System.Runtime.Extensions.dll", - "ref/netcore50/System.Runtime.Extensions.xml", - "ref/netcore50/de/System.Runtime.Extensions.xml", - "ref/netcore50/es/System.Runtime.Extensions.xml", - "ref/netcore50/fr/System.Runtime.Extensions.xml", - "ref/netcore50/it/System.Runtime.Extensions.xml", - "ref/netcore50/ja/System.Runtime.Extensions.xml", - "ref/netcore50/ko/System.Runtime.Extensions.xml", - "ref/netcore50/ru/System.Runtime.Extensions.xml", - "ref/netcore50/zh-hans/System.Runtime.Extensions.xml", - "ref/netcore50/zh-hant/System.Runtime.Extensions.xml", - "ref/netstandard1.0/System.Runtime.Extensions.dll", - "ref/netstandard1.0/System.Runtime.Extensions.xml", - "ref/netstandard1.0/de/System.Runtime.Extensions.xml", - "ref/netstandard1.0/es/System.Runtime.Extensions.xml", - "ref/netstandard1.0/fr/System.Runtime.Extensions.xml", - "ref/netstandard1.0/it/System.Runtime.Extensions.xml", - "ref/netstandard1.0/ja/System.Runtime.Extensions.xml", - "ref/netstandard1.0/ko/System.Runtime.Extensions.xml", - "ref/netstandard1.0/ru/System.Runtime.Extensions.xml", - "ref/netstandard1.0/zh-hans/System.Runtime.Extensions.xml", - "ref/netstandard1.0/zh-hant/System.Runtime.Extensions.xml", - "ref/netstandard1.3/System.Runtime.Extensions.dll", - "ref/netstandard1.3/System.Runtime.Extensions.xml", - "ref/netstandard1.3/de/System.Runtime.Extensions.xml", - "ref/netstandard1.3/es/System.Runtime.Extensions.xml", - "ref/netstandard1.3/fr/System.Runtime.Extensions.xml", - "ref/netstandard1.3/it/System.Runtime.Extensions.xml", - "ref/netstandard1.3/ja/System.Runtime.Extensions.xml", - "ref/netstandard1.3/ko/System.Runtime.Extensions.xml", - "ref/netstandard1.3/ru/System.Runtime.Extensions.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.Extensions.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.Extensions.xml", - "ref/netstandard1.5/System.Runtime.Extensions.dll", - "ref/netstandard1.5/System.Runtime.Extensions.xml", - "ref/netstandard1.5/de/System.Runtime.Extensions.xml", - "ref/netstandard1.5/es/System.Runtime.Extensions.xml", - "ref/netstandard1.5/fr/System.Runtime.Extensions.xml", - "ref/netstandard1.5/it/System.Runtime.Extensions.xml", - "ref/netstandard1.5/ja/System.Runtime.Extensions.xml", - "ref/netstandard1.5/ko/System.Runtime.Extensions.xml", - "ref/netstandard1.5/ru/System.Runtime.Extensions.xml", - "ref/netstandard1.5/zh-hans/System.Runtime.Extensions.xml", - "ref/netstandard1.5/zh-hant/System.Runtime.Extensions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.extensions.4.3.0.nupkg.sha512", - "system.runtime.extensions.nuspec" - ] - }, - "System.Runtime.Handles/4.3.0": { - "sha512": "CluvHdVUv54BvLTOCCyybugreDNk/rR8unMPruzXDtxSjvrQOU3M4R831/lQf4YI8VYp668FGQa/01E+Rq8PEQ==", - "type": "package", - "path": "system.runtime.handles/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/_._", - "ref/netstandard1.3/System.Runtime.Handles.dll", - "ref/netstandard1.3/System.Runtime.Handles.xml", - "ref/netstandard1.3/de/System.Runtime.Handles.xml", - "ref/netstandard1.3/es/System.Runtime.Handles.xml", - "ref/netstandard1.3/fr/System.Runtime.Handles.xml", - "ref/netstandard1.3/it/System.Runtime.Handles.xml", - "ref/netstandard1.3/ja/System.Runtime.Handles.xml", - "ref/netstandard1.3/ko/System.Runtime.Handles.xml", - "ref/netstandard1.3/ru/System.Runtime.Handles.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.Handles.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.Handles.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.handles.4.3.0.nupkg.sha512", - "system.runtime.handles.nuspec" - ] - }, - "System.Runtime.InteropServices/4.3.0": { - "sha512": "ZQeZw+ZU77ua1nFXycYM5G8oioFZe+N84qC/XUg1BEBl7z9luZcyjLu7+4H0yJuNfn1hOAiAAZ3u5us/lj9w2Q==", - "type": "package", - "path": "system.runtime.interopservices/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Runtime.InteropServices.dll", - "lib/net463/System.Runtime.InteropServices.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Runtime.InteropServices.dll", - "ref/net463/System.Runtime.InteropServices.dll", - "ref/netcore50/System.Runtime.InteropServices.dll", - "ref/netcore50/System.Runtime.InteropServices.xml", - "ref/netcore50/de/System.Runtime.InteropServices.xml", - "ref/netcore50/es/System.Runtime.InteropServices.xml", - "ref/netcore50/fr/System.Runtime.InteropServices.xml", - "ref/netcore50/it/System.Runtime.InteropServices.xml", - "ref/netcore50/ja/System.Runtime.InteropServices.xml", - "ref/netcore50/ko/System.Runtime.InteropServices.xml", - "ref/netcore50/ru/System.Runtime.InteropServices.xml", - "ref/netcore50/zh-hans/System.Runtime.InteropServices.xml", - "ref/netcore50/zh-hant/System.Runtime.InteropServices.xml", - "ref/netcoreapp1.1/System.Runtime.InteropServices.dll", - "ref/netstandard1.1/System.Runtime.InteropServices.dll", - "ref/netstandard1.1/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/zh-hant/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/System.Runtime.InteropServices.dll", - "ref/netstandard1.2/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/zh-hant/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/System.Runtime.InteropServices.dll", - "ref/netstandard1.3/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/System.Runtime.InteropServices.dll", - "ref/netstandard1.5/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/zh-hant/System.Runtime.InteropServices.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.interopservices.4.3.0.nupkg.sha512", - "system.runtime.interopservices.nuspec" - ] - }, - "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { - "sha512": "b0kFMpo8yeYtJ0yIXyde4xxa9Xpsn9GlCA0DnLdI4Cd77z3IzkKGPKx4NlCE4AoDInm/PStyVKSfP7FWaimtGw==", - "type": "package", - "path": "system.runtime.interopservices.runtimeinformation/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/win8/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/wpa81/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/unix/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/win/lib/net45/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/win/lib/netcore50/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/win/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "system.runtime.interopservices.runtimeinformation.4.3.0.nupkg.sha512", - "system.runtime.interopservices.runtimeinformation.nuspec" - ] - }, - "System.Runtime.InteropServices.WindowsRuntime/4.3.0": { - "sha512": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==", - "type": "package", - "path": "system.runtime.interopservices.windowsruntime/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Runtime.InteropServices.WindowsRuntime.dll", - "lib/netstandard1.3/System.Runtime.InteropServices.WindowsRuntime.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios1/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Runtime.InteropServices.WindowsRuntime.dll", - "ref/netcore50/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/de/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/es/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/fr/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/it/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/ja/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/ko/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/ru/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/zh-hans/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/zh-hant/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/System.Runtime.InteropServices.WindowsRuntime.dll", - "ref/netstandard1.0/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/de/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/es/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/fr/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/it/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/ja/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/ko/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/ru/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/zh-hans/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/zh-hant/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Runtime.InteropServices.WindowsRuntime.dll", - "system.runtime.interopservices.windowsruntime.4.3.0.nupkg.sha512", - "system.runtime.interopservices.windowsruntime.nuspec" - ] - }, - "System.Runtime.Numerics/4.3.0": { - "sha512": "PjR/qo5+xITUgeU7HCGf4c40augniiFLRQjPDiM8Fie9nGxsfGVOjB9BQycYON3ZWT9joQQ1d62HxA45Kvf9NA==", - "type": "package", - "path": "system.runtime.numerics/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Runtime.Numerics.dll", - "lib/netstandard1.3/System.Runtime.Numerics.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Runtime.Numerics.dll", - "ref/netcore50/System.Runtime.Numerics.xml", - "ref/netcore50/de/System.Runtime.Numerics.xml", - "ref/netcore50/es/System.Runtime.Numerics.xml", - "ref/netcore50/fr/System.Runtime.Numerics.xml", - "ref/netcore50/it/System.Runtime.Numerics.xml", - "ref/netcore50/ja/System.Runtime.Numerics.xml", - "ref/netcore50/ko/System.Runtime.Numerics.xml", - "ref/netcore50/ru/System.Runtime.Numerics.xml", - "ref/netcore50/zh-hans/System.Runtime.Numerics.xml", - "ref/netcore50/zh-hant/System.Runtime.Numerics.xml", - "ref/netstandard1.1/System.Runtime.Numerics.dll", - "ref/netstandard1.1/System.Runtime.Numerics.xml", - "ref/netstandard1.1/de/System.Runtime.Numerics.xml", - "ref/netstandard1.1/es/System.Runtime.Numerics.xml", - "ref/netstandard1.1/fr/System.Runtime.Numerics.xml", - "ref/netstandard1.1/it/System.Runtime.Numerics.xml", - "ref/netstandard1.1/ja/System.Runtime.Numerics.xml", - "ref/netstandard1.1/ko/System.Runtime.Numerics.xml", - "ref/netstandard1.1/ru/System.Runtime.Numerics.xml", - "ref/netstandard1.1/zh-hans/System.Runtime.Numerics.xml", - "ref/netstandard1.1/zh-hant/System.Runtime.Numerics.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.numerics.4.3.0.nupkg.sha512", - "system.runtime.numerics.nuspec" - ] - }, - "System.Security.AccessControl/4.5.0": { - "sha512": "6RQtcT+TyDgLUFsAnmmdfRRGdLYKxSZGkSPOd052tvYFxOTMaQb6ANlwhGqZtNO52PosaCoXu7uatFneujAxmA==", - "type": "package", - "path": "system.security.accesscontrol/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net46/System.Security.AccessControl.dll", - "lib/net461/System.Security.AccessControl.dll", - "lib/netstandard1.3/System.Security.AccessControl.dll", - "lib/netstandard2.0/System.Security.AccessControl.dll", - "lib/uap10.0.16299/_._", - "ref/net46/System.Security.AccessControl.dll", - "ref/net461/System.Security.AccessControl.dll", - "ref/net461/System.Security.AccessControl.xml", - "ref/netstandard1.3/System.Security.AccessControl.dll", - "ref/netstandard1.3/System.Security.AccessControl.xml", - "ref/netstandard1.3/de/System.Security.AccessControl.xml", - "ref/netstandard1.3/es/System.Security.AccessControl.xml", - "ref/netstandard1.3/fr/System.Security.AccessControl.xml", - "ref/netstandard1.3/it/System.Security.AccessControl.xml", - "ref/netstandard1.3/ja/System.Security.AccessControl.xml", - "ref/netstandard1.3/ko/System.Security.AccessControl.xml", - "ref/netstandard1.3/ru/System.Security.AccessControl.xml", - "ref/netstandard1.3/zh-hans/System.Security.AccessControl.xml", - "ref/netstandard1.3/zh-hant/System.Security.AccessControl.xml", - "ref/netstandard2.0/System.Security.AccessControl.dll", - "ref/netstandard2.0/System.Security.AccessControl.xml", - "ref/uap10.0.16299/_._", - "runtimes/win/lib/net46/System.Security.AccessControl.dll", - "runtimes/win/lib/net461/System.Security.AccessControl.dll", - "runtimes/win/lib/netcoreapp2.0/System.Security.AccessControl.dll", - "runtimes/win/lib/netstandard1.3/System.Security.AccessControl.dll", - "runtimes/win/lib/uap10.0.16299/_._", - "system.security.accesscontrol.4.5.0.nupkg.sha512", - "system.security.accesscontrol.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Security.Claims/4.3.0": { - "sha512": "q3K5CAH2wFGisxZFRI7r/KdGQrPPodUfgOIaDQ161E0zZt6hOTR+KFJ4G387roIN8Ww+sYiiyWJE3wU5Ttcshg==", - "type": "package", - "path": "system.security.claims/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Claims.dll", - "lib/netstandard1.3/System.Security.Claims.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Claims.dll", - "ref/netstandard1.3/System.Security.Claims.dll", - "ref/netstandard1.3/System.Security.Claims.xml", - "ref/netstandard1.3/de/System.Security.Claims.xml", - "ref/netstandard1.3/es/System.Security.Claims.xml", - "ref/netstandard1.3/fr/System.Security.Claims.xml", - "ref/netstandard1.3/it/System.Security.Claims.xml", - "ref/netstandard1.3/ja/System.Security.Claims.xml", - "ref/netstandard1.3/ko/System.Security.Claims.xml", - "ref/netstandard1.3/ru/System.Security.Claims.xml", - "ref/netstandard1.3/zh-hans/System.Security.Claims.xml", - "ref/netstandard1.3/zh-hant/System.Security.Claims.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.security.claims.4.3.0.nupkg.sha512", - "system.security.claims.nuspec" - ] - }, - "System.Security.Cryptography.Algorithms/4.3.1": { - "sha512": "DVUblnRfnarrI5olEC2B/OCsJQd0anjVaObQMndHSc43efbc88/RMOlDyg/EyY0ix5ecyZMXS8zMksb5ukebZA==", - "type": "package", - "path": "system.security.cryptography.algorithms/4.3.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Algorithms.dll", - "lib/net461/System.Security.Cryptography.Algorithms.dll", - "lib/net463/System.Security.Cryptography.Algorithms.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Algorithms.dll", - "ref/net461/System.Security.Cryptography.Algorithms.dll", - "ref/net463/System.Security.Cryptography.Algorithms.dll", - "ref/netstandard1.3/System.Security.Cryptography.Algorithms.dll", - "ref/netstandard1.4/System.Security.Cryptography.Algorithms.dll", - "ref/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/osx/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/net461/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/net463/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/netcore50/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "system.security.cryptography.algorithms.4.3.1.nupkg.sha512", - "system.security.cryptography.algorithms.nuspec" - ] - }, - "System.Security.Cryptography.Cng/4.3.0": { - "sha512": "YnInNBTqp3fnjcobXsu99l6WWZCAgq6pJN8JdecfTBtH+FYX7fkOrVcHjClROhYMpi8SO+n58znfuck4aET16g==", - "type": "package", - "path": "system.security.cryptography.cng/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/net46/System.Security.Cryptography.Cng.dll", - "lib/net461/System.Security.Cryptography.Cng.dll", - "lib/net463/System.Security.Cryptography.Cng.dll", - "ref/net46/System.Security.Cryptography.Cng.dll", - "ref/net461/System.Security.Cryptography.Cng.dll", - "ref/net463/System.Security.Cryptography.Cng.dll", - "ref/netstandard1.3/System.Security.Cryptography.Cng.dll", - "ref/netstandard1.4/System.Security.Cryptography.Cng.dll", - "ref/netstandard1.6/System.Security.Cryptography.Cng.dll", - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/net461/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/net463/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/netstandard1.4/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Cng.dll", - "system.security.cryptography.cng.4.3.0.nupkg.sha512", - "system.security.cryptography.cng.nuspec" - ] - }, - "System.Security.Cryptography.Csp/4.3.0": { - "sha512": "QzF1kXR6GPUvaDGH4Jrf4OA1c+baxDC/O6E/RAzbHHux+SBTadXzsqDz/flgTVuh5tlKiZol0sUz5FMzhXjzUQ==", - "type": "package", - "path": "system.security.cryptography.csp/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Csp.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Csp.dll", - "ref/netstandard1.3/System.Security.Cryptography.Csp.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Csp.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.Csp.dll", - "runtimes/win/lib/netcore50/_._", - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Csp.dll", - "system.security.cryptography.csp.4.3.0.nupkg.sha512", - "system.security.cryptography.csp.nuspec" - ] - }, - "System.Security.Cryptography.Encoding/4.3.0": { - "sha512": "XCat0j5jVC83UG9fofcuiYDwN0PVKc2OWD0QVLjYpXn7dz+gNaANkHPbhNtr5PR8rDQNHrxtI912Hb29YAB14A==", - "type": "package", - "path": "system.security.cryptography.encoding/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Encoding.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Encoding.dll", - "ref/netstandard1.3/System.Security.Cryptography.Encoding.dll", - "ref/netstandard1.3/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/de/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/es/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/fr/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/it/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/ja/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/ko/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/ru/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/zh-hans/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/zh-hant/System.Security.Cryptography.Encoding.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.Encoding.dll", - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll", - "system.security.cryptography.encoding.4.3.0.nupkg.sha512", - "system.security.cryptography.encoding.nuspec" - ] - }, - "System.Security.Cryptography.OpenSsl/4.3.0": { - "sha512": "ZFMKGUiXMPhz+MaOayRRNeomDALWhZGIAmF2g1jQFFeVEyul7od3QYIv8F3NDGHtyidpbvmej5MCohyt87Eynw==", - "type": "package", - "path": "system.security.cryptography.openssl/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", - "ref/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", - "system.security.cryptography.openssl.4.3.0.nupkg.sha512", - "system.security.cryptography.openssl.nuspec" - ] - }, - "System.Security.Cryptography.Primitives/4.3.0": { - "sha512": "WtgnP5mOu5zKL3vQMUPT9tV7XVYGV7Jtb0540DgBD7MMN5ojonwIcw8VybZvS6VloGmE7CRt/Hms8adBsN1DRw==", - "type": "package", - "path": "system.security.cryptography.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Primitives.dll", - "lib/netstandard1.3/System.Security.Cryptography.Primitives.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Primitives.dll", - "ref/netstandard1.3/System.Security.Cryptography.Primitives.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.security.cryptography.primitives.4.3.0.nupkg.sha512", - "system.security.cryptography.primitives.nuspec" - ] - }, - "System.Security.Cryptography.X509Certificates/4.3.0": { - "sha512": "MY2Gq1Uo4rRE7D5LmCTBvoK7k9tRPqs0sjjkhviGxNdDEO2CwhEEAf5c15Dk2X9KAjoLLVwuKZUtP9AuQnNNAA==", - "type": "package", - "path": "system.security.cryptography.x509certificates/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.X509Certificates.dll", - "lib/net461/System.Security.Cryptography.X509Certificates.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.X509Certificates.dll", - "ref/net461/System.Security.Cryptography.X509Certificates.dll", - "ref/netstandard1.3/System.Security.Cryptography.X509Certificates.dll", - "ref/netstandard1.3/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/de/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/es/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/fr/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/it/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/ja/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/ko/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/ru/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/zh-hans/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/zh-hant/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.dll", - "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/de/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/es/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/fr/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/it/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/ja/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/ko/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/ru/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/zh-hans/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/zh-hant/System.Security.Cryptography.X509Certificates.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/net461/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/netcore50/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll", - "system.security.cryptography.x509certificates.4.3.0.nupkg.sha512", - "system.security.cryptography.x509certificates.nuspec" - ] - }, - "System.Security.Principal/4.3.0": { - "sha512": "24oe0NGJY32e+DFHVQzl2okM9uwYmn0Aa6nehqtVZ55/Al4Yva7S3BN934Kn5qATH7TVTUJkgxhisdfF7mKDfg==", - "type": "package", - "path": "system.security.principal/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Security.Principal.dll", - "lib/netstandard1.0/System.Security.Principal.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Security.Principal.dll", - "ref/netcore50/System.Security.Principal.xml", - "ref/netcore50/de/System.Security.Principal.xml", - "ref/netcore50/es/System.Security.Principal.xml", - "ref/netcore50/fr/System.Security.Principal.xml", - "ref/netcore50/it/System.Security.Principal.xml", - "ref/netcore50/ja/System.Security.Principal.xml", - "ref/netcore50/ko/System.Security.Principal.xml", - "ref/netcore50/ru/System.Security.Principal.xml", - "ref/netcore50/zh-hans/System.Security.Principal.xml", - "ref/netcore50/zh-hant/System.Security.Principal.xml", - "ref/netstandard1.0/System.Security.Principal.dll", - "ref/netstandard1.0/System.Security.Principal.xml", - "ref/netstandard1.0/de/System.Security.Principal.xml", - "ref/netstandard1.0/es/System.Security.Principal.xml", - "ref/netstandard1.0/fr/System.Security.Principal.xml", - "ref/netstandard1.0/it/System.Security.Principal.xml", - "ref/netstandard1.0/ja/System.Security.Principal.xml", - "ref/netstandard1.0/ko/System.Security.Principal.xml", - "ref/netstandard1.0/ru/System.Security.Principal.xml", - "ref/netstandard1.0/zh-hans/System.Security.Principal.xml", - "ref/netstandard1.0/zh-hant/System.Security.Principal.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.security.principal.4.3.0.nupkg.sha512", - "system.security.principal.nuspec" - ] - }, - "System.Security.Principal.Windows/4.5.0": { - "sha512": "hs2zF4tOQ3V4iQttVnLrnR/i8AOrrAgu2Gmp4/jNaE/+5hiZWDj20FK/m/OW3Itdi9XDvqf55WzHkiWYtOSUNg==", - "type": "package", - "path": "system.security.principal.windows/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net46/System.Security.Principal.Windows.dll", - "lib/net461/System.Security.Principal.Windows.dll", - "lib/netstandard1.3/System.Security.Principal.Windows.dll", - "lib/netstandard2.0/System.Security.Principal.Windows.dll", - "lib/uap10.0.16299/_._", - "ref/net46/System.Security.Principal.Windows.dll", - "ref/net461/System.Security.Principal.Windows.dll", - "ref/net461/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/System.Security.Principal.Windows.dll", - "ref/netstandard1.3/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/de/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/es/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/fr/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/it/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/ja/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/ko/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/ru/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/zh-hans/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/zh-hant/System.Security.Principal.Windows.xml", - "ref/netstandard2.0/System.Security.Principal.Windows.dll", - "ref/netstandard2.0/System.Security.Principal.Windows.xml", - "ref/uap10.0.16299/_._", - "runtimes/unix/lib/netcoreapp2.0/System.Security.Principal.Windows.dll", - "runtimes/win/lib/net46/System.Security.Principal.Windows.dll", - "runtimes/win/lib/net461/System.Security.Principal.Windows.dll", - "runtimes/win/lib/netcoreapp2.0/System.Security.Principal.Windows.dll", - "runtimes/win/lib/netstandard1.3/System.Security.Principal.Windows.dll", - "runtimes/win/lib/uap10.0.16299/_._", - "system.security.principal.windows.4.5.0.nupkg.sha512", - "system.security.principal.windows.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.ServiceModel.Primitives/4.5.3": { - "sha512": "Wc9Hgg4Cmqi416zvEgq2sW1YYCGuhwWzspDclJWlFZqY6EGhFUPZU+kVpl5z9kAgrSOQP7/Uiik+PtSQtmq+5A==", - "type": "package", - "path": "system.servicemodel.primitives/4.5.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.ServiceModel.Primitives.dll", - "lib/net461/System.ServiceModel.Primitives.dll", - "lib/netcore50/System.ServiceModel.Primitives.dll", - "lib/netstandard1.3/System.ServiceModel.Primitives.dll", - "lib/netstandard2.0/System.ServiceModel.Primitives.dll", - "lib/netstandard2.0/System.ServiceModel.dll", - "lib/portable-net45+win8+wp8/_._", - "lib/uap10.0.16300/System.ServiceModel.Primitives.dll", - "lib/uap10.0.16300/System.ServiceModel.dll", - "lib/win8/_._", - "lib/wp8/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.ServiceModel.Primitives.dll", - "ref/net461/System.ServiceModel.Primitives.dll", - "ref/netcore50/System.ServiceModel.Primitives.dll", - "ref/netstandard1.0/System.ServiceModel.Primitives.dll", - "ref/netstandard1.1/System.ServiceModel.Primitives.dll", - "ref/netstandard1.3/System.ServiceModel.Primitives.dll", - "ref/netstandard2.0/System.ServiceModel.Primitives.dll", - "ref/netstandard2.0/System.ServiceModel.dll", - "ref/portable-net45+win8+wp8/_._", - "ref/uap10.0.16300/System.ServiceModel.Primitives.dll", - "ref/uap10.0.16300/System.ServiceModel.dll", - "ref/win8/_._", - "ref/wp8/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.servicemodel.primitives.4.5.3.nupkg.sha512", - "system.servicemodel.primitives.nuspec", - "version.txt" - ] - }, - "System.Text.Encoding/4.3.0": { - "sha512": "b/f+7HMTpxIfeV7H03bkuHKMFylCGfr9/U6gePnfFFW0aF8LOWLDgQCY6V1oWUqDksC3mdNuyChM1vy9TP4sZw==", - "type": "package", - "path": "system.text.encoding/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Text.Encoding.dll", - "ref/netcore50/System.Text.Encoding.xml", - "ref/netcore50/de/System.Text.Encoding.xml", - "ref/netcore50/es/System.Text.Encoding.xml", - "ref/netcore50/fr/System.Text.Encoding.xml", - "ref/netcore50/it/System.Text.Encoding.xml", - "ref/netcore50/ja/System.Text.Encoding.xml", - "ref/netcore50/ko/System.Text.Encoding.xml", - "ref/netcore50/ru/System.Text.Encoding.xml", - "ref/netcore50/zh-hans/System.Text.Encoding.xml", - "ref/netcore50/zh-hant/System.Text.Encoding.xml", - "ref/netstandard1.0/System.Text.Encoding.dll", - "ref/netstandard1.0/System.Text.Encoding.xml", - "ref/netstandard1.0/de/System.Text.Encoding.xml", - "ref/netstandard1.0/es/System.Text.Encoding.xml", - "ref/netstandard1.0/fr/System.Text.Encoding.xml", - "ref/netstandard1.0/it/System.Text.Encoding.xml", - "ref/netstandard1.0/ja/System.Text.Encoding.xml", - "ref/netstandard1.0/ko/System.Text.Encoding.xml", - "ref/netstandard1.0/ru/System.Text.Encoding.xml", - "ref/netstandard1.0/zh-hans/System.Text.Encoding.xml", - "ref/netstandard1.0/zh-hant/System.Text.Encoding.xml", - "ref/netstandard1.3/System.Text.Encoding.dll", - "ref/netstandard1.3/System.Text.Encoding.xml", - "ref/netstandard1.3/de/System.Text.Encoding.xml", - "ref/netstandard1.3/es/System.Text.Encoding.xml", - "ref/netstandard1.3/fr/System.Text.Encoding.xml", - "ref/netstandard1.3/it/System.Text.Encoding.xml", - "ref/netstandard1.3/ja/System.Text.Encoding.xml", - "ref/netstandard1.3/ko/System.Text.Encoding.xml", - "ref/netstandard1.3/ru/System.Text.Encoding.xml", - "ref/netstandard1.3/zh-hans/System.Text.Encoding.xml", - "ref/netstandard1.3/zh-hant/System.Text.Encoding.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.text.encoding.4.3.0.nupkg.sha512", - "system.text.encoding.nuspec" - ] - }, - "System.Text.Encoding.Extensions/4.3.0": { - "sha512": "5kjF3HgeNc8AxcyOfkLoFbljz4+3iOioF/m1PjGLK0Li96VW6cPGS/L2ov1GFfJqtPDU63E6AVHnHgrz/pw+7Q==", - "type": "package", - "path": "system.text.encoding.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Text.Encoding.Extensions.dll", - "ref/netcore50/System.Text.Encoding.Extensions.xml", - "ref/netcore50/de/System.Text.Encoding.Extensions.xml", - "ref/netcore50/es/System.Text.Encoding.Extensions.xml", - "ref/netcore50/fr/System.Text.Encoding.Extensions.xml", - "ref/netcore50/it/System.Text.Encoding.Extensions.xml", - "ref/netcore50/ja/System.Text.Encoding.Extensions.xml", - "ref/netcore50/ko/System.Text.Encoding.Extensions.xml", - "ref/netcore50/ru/System.Text.Encoding.Extensions.xml", - "ref/netcore50/zh-hans/System.Text.Encoding.Extensions.xml", - "ref/netcore50/zh-hant/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/System.Text.Encoding.Extensions.dll", - "ref/netstandard1.0/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/de/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/es/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/fr/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/it/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/ja/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/ko/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/ru/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/zh-hans/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/zh-hant/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/System.Text.Encoding.Extensions.dll", - "ref/netstandard1.3/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/de/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/es/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/fr/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/it/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/ja/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/ko/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/ru/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/zh-hans/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/zh-hant/System.Text.Encoding.Extensions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.text.encoding.extensions.4.3.0.nupkg.sha512", - "system.text.encoding.extensions.nuspec" - ] - }, - "System.Text.RegularExpressions/4.3.0": { - "sha512": "gDU8FI3zDZosA+4QpiTZG2TXzMMhjLlmNEz6cGV/C1nIZ/7Sq5QFf2SrKBrZMYNT8lwjN1wA4TdrZYmuCnCq0w==", - "type": "package", - "path": "system.text.regularexpressions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net463/System.Text.RegularExpressions.dll", - "lib/netcore50/System.Text.RegularExpressions.dll", - "lib/netstandard1.6/System.Text.RegularExpressions.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net463/System.Text.RegularExpressions.dll", - "ref/netcore50/System.Text.RegularExpressions.dll", - "ref/netcore50/System.Text.RegularExpressions.xml", - "ref/netcore50/de/System.Text.RegularExpressions.xml", - "ref/netcore50/es/System.Text.RegularExpressions.xml", - "ref/netcore50/fr/System.Text.RegularExpressions.xml", - "ref/netcore50/it/System.Text.RegularExpressions.xml", - "ref/netcore50/ja/System.Text.RegularExpressions.xml", - "ref/netcore50/ko/System.Text.RegularExpressions.xml", - "ref/netcore50/ru/System.Text.RegularExpressions.xml", - "ref/netcore50/zh-hans/System.Text.RegularExpressions.xml", - "ref/netcore50/zh-hant/System.Text.RegularExpressions.xml", - "ref/netcoreapp1.1/System.Text.RegularExpressions.dll", - "ref/netstandard1.0/System.Text.RegularExpressions.dll", - "ref/netstandard1.0/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/de/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/es/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/fr/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/it/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/ja/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/ko/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/ru/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/zh-hans/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/zh-hant/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/System.Text.RegularExpressions.dll", - "ref/netstandard1.3/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/de/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/es/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/fr/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/it/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/ja/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/ko/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/ru/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/zh-hans/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/zh-hant/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/System.Text.RegularExpressions.dll", - "ref/netstandard1.6/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/de/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/es/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/fr/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/it/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/ja/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/ko/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/ru/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/zh-hans/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/zh-hant/System.Text.RegularExpressions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.text.regularexpressions.4.3.0.nupkg.sha512", - "system.text.regularexpressions.nuspec" - ] - }, - "System.Threading/4.3.0": { - "sha512": "l6J1G9zmn6r5xU+DSp/Vxgx6eG+qUvQgdpgo28m1gEwfNyG6HqlF6h2ESDXZCYEPnngsmkTQ+q7MyyMMTNlaiA==", - "type": "package", - "path": "system.threading/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Threading.dll", - "lib/netstandard1.3/System.Threading.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Threading.dll", - "ref/netcore50/System.Threading.xml", - "ref/netcore50/de/System.Threading.xml", - "ref/netcore50/es/System.Threading.xml", - "ref/netcore50/fr/System.Threading.xml", - "ref/netcore50/it/System.Threading.xml", - "ref/netcore50/ja/System.Threading.xml", - "ref/netcore50/ko/System.Threading.xml", - "ref/netcore50/ru/System.Threading.xml", - "ref/netcore50/zh-hans/System.Threading.xml", - "ref/netcore50/zh-hant/System.Threading.xml", - "ref/netstandard1.0/System.Threading.dll", - "ref/netstandard1.0/System.Threading.xml", - "ref/netstandard1.0/de/System.Threading.xml", - "ref/netstandard1.0/es/System.Threading.xml", - "ref/netstandard1.0/fr/System.Threading.xml", - "ref/netstandard1.0/it/System.Threading.xml", - "ref/netstandard1.0/ja/System.Threading.xml", - "ref/netstandard1.0/ko/System.Threading.xml", - "ref/netstandard1.0/ru/System.Threading.xml", - "ref/netstandard1.0/zh-hans/System.Threading.xml", - "ref/netstandard1.0/zh-hant/System.Threading.xml", - "ref/netstandard1.3/System.Threading.dll", - "ref/netstandard1.3/System.Threading.xml", - "ref/netstandard1.3/de/System.Threading.xml", - "ref/netstandard1.3/es/System.Threading.xml", - "ref/netstandard1.3/fr/System.Threading.xml", - "ref/netstandard1.3/it/System.Threading.xml", - "ref/netstandard1.3/ja/System.Threading.xml", - "ref/netstandard1.3/ko/System.Threading.xml", - "ref/netstandard1.3/ru/System.Threading.xml", - "ref/netstandard1.3/zh-hans/System.Threading.xml", - "ref/netstandard1.3/zh-hant/System.Threading.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Threading.dll", - "system.threading.4.3.0.nupkg.sha512", - "system.threading.nuspec" - ] - }, - "System.Threading.Overlapped/4.3.0": { - "sha512": "m3HQ2dPiX/DSTpf+yJt8B0c+SRvzfqAJKx+QDWi+VLhz8svLT23MVjEOHPF/KiSLeArKU/iHescrbLd3yVgyNg==", - "type": "package", - "path": "system.threading.overlapped/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/net46/System.Threading.Overlapped.dll", - "ref/net46/System.Threading.Overlapped.dll", - "ref/netstandard1.3/System.Threading.Overlapped.dll", - "ref/netstandard1.3/System.Threading.Overlapped.xml", - "ref/netstandard1.3/de/System.Threading.Overlapped.xml", - "ref/netstandard1.3/es/System.Threading.Overlapped.xml", - "ref/netstandard1.3/fr/System.Threading.Overlapped.xml", - "ref/netstandard1.3/it/System.Threading.Overlapped.xml", - "ref/netstandard1.3/ja/System.Threading.Overlapped.xml", - "ref/netstandard1.3/ko/System.Threading.Overlapped.xml", - "ref/netstandard1.3/ru/System.Threading.Overlapped.xml", - "ref/netstandard1.3/zh-hans/System.Threading.Overlapped.xml", - "ref/netstandard1.3/zh-hant/System.Threading.Overlapped.xml", - "runtimes/unix/lib/netstandard1.3/System.Threading.Overlapped.dll", - "runtimes/win/lib/net46/System.Threading.Overlapped.dll", - "runtimes/win/lib/netcore50/System.Threading.Overlapped.dll", - "runtimes/win/lib/netstandard1.3/System.Threading.Overlapped.dll", - "system.threading.overlapped.4.3.0.nupkg.sha512", - "system.threading.overlapped.nuspec" - ] - }, - "System.Threading.Tasks/4.3.0": { - "sha512": "fUiP+CyyCjs872OA8trl6p97qma/da1xGq3h4zAbJZk8zyaU4zyEfqW5vbkP80xG/Nimun1vlWBboMEk7XxdEw==", - "type": "package", - "path": "system.threading.tasks/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Threading.Tasks.dll", - "ref/netcore50/System.Threading.Tasks.xml", - "ref/netcore50/de/System.Threading.Tasks.xml", - "ref/netcore50/es/System.Threading.Tasks.xml", - "ref/netcore50/fr/System.Threading.Tasks.xml", - "ref/netcore50/it/System.Threading.Tasks.xml", - "ref/netcore50/ja/System.Threading.Tasks.xml", - "ref/netcore50/ko/System.Threading.Tasks.xml", - "ref/netcore50/ru/System.Threading.Tasks.xml", - "ref/netcore50/zh-hans/System.Threading.Tasks.xml", - "ref/netcore50/zh-hant/System.Threading.Tasks.xml", - "ref/netstandard1.0/System.Threading.Tasks.dll", - "ref/netstandard1.0/System.Threading.Tasks.xml", - "ref/netstandard1.0/de/System.Threading.Tasks.xml", - "ref/netstandard1.0/es/System.Threading.Tasks.xml", - "ref/netstandard1.0/fr/System.Threading.Tasks.xml", - "ref/netstandard1.0/it/System.Threading.Tasks.xml", - "ref/netstandard1.0/ja/System.Threading.Tasks.xml", - "ref/netstandard1.0/ko/System.Threading.Tasks.xml", - "ref/netstandard1.0/ru/System.Threading.Tasks.xml", - "ref/netstandard1.0/zh-hans/System.Threading.Tasks.xml", - "ref/netstandard1.0/zh-hant/System.Threading.Tasks.xml", - "ref/netstandard1.3/System.Threading.Tasks.dll", - "ref/netstandard1.3/System.Threading.Tasks.xml", - "ref/netstandard1.3/de/System.Threading.Tasks.xml", - "ref/netstandard1.3/es/System.Threading.Tasks.xml", - "ref/netstandard1.3/fr/System.Threading.Tasks.xml", - "ref/netstandard1.3/it/System.Threading.Tasks.xml", - "ref/netstandard1.3/ja/System.Threading.Tasks.xml", - "ref/netstandard1.3/ko/System.Threading.Tasks.xml", - "ref/netstandard1.3/ru/System.Threading.Tasks.xml", - "ref/netstandard1.3/zh-hans/System.Threading.Tasks.xml", - "ref/netstandard1.3/zh-hant/System.Threading.Tasks.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.tasks.4.3.0.nupkg.sha512", - "system.threading.tasks.nuspec" - ] - }, - "System.Threading.Tasks.Dataflow/4.9.0": { - "sha512": "dTS+3D/GtG2/Pvc3E5YzVvAa7aQJgLDlZDIzukMOJjYudVOQOUXEU68y6Zi3Nn/jqIeB5kOCwrGbQFAKHVzXEQ==", - "type": "package", - "path": "system.threading.tasks.dataflow/4.9.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Threading.Tasks.Dataflow.dll", - "lib/netstandard1.0/System.Threading.Tasks.Dataflow.xml", - "lib/netstandard1.1/System.Threading.Tasks.Dataflow.dll", - "lib/netstandard1.1/System.Threading.Tasks.Dataflow.xml", - "lib/netstandard2.0/System.Threading.Tasks.Dataflow.dll", - "lib/netstandard2.0/System.Threading.Tasks.Dataflow.xml", - "lib/portable-net45+win8+wpa81/System.Threading.Tasks.Dataflow.dll", - "lib/portable-net45+win8+wpa81/System.Threading.Tasks.Dataflow.xml", - "system.threading.tasks.dataflow.4.9.0.nupkg.sha512", - "system.threading.tasks.dataflow.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Threading.Tasks.Extensions/4.5.2": { - "sha512": "BG/TNxDFv0svAzx8OiMXDlsHfGw623BZ8tCXw4YLhDFDvDhNUEV58jKYMGRnkbJNm7c3JNNJDiN7JBMzxRBR2w==", - "type": "package", - "path": "system.threading.tasks.extensions/4.5.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/netcoreapp2.1/_._", - "lib/netstandard1.0/System.Threading.Tasks.Extensions.dll", - "lib/netstandard1.0/System.Threading.Tasks.Extensions.xml", - "lib/netstandard2.0/System.Threading.Tasks.Extensions.dll", - "lib/netstandard2.0/System.Threading.Tasks.Extensions.xml", - "lib/portable-net45+win8+wp8+wpa81/System.Threading.Tasks.Extensions.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Threading.Tasks.Extensions.xml", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/netcoreapp2.1/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.tasks.extensions.4.5.2.nupkg.sha512", - "system.threading.tasks.extensions.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Threading.Thread/4.3.0": { - "sha512": "z+EramDnni9/yneaURFT1bDcrlnqGxFgb2Mn2/izxWXiVR6OytpVjmLdO2hLXJ1nZXUCUEjt+9OYj69/cjWl/g==", - "type": "package", - "path": "system.threading.thread/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Threading.Thread.dll", - "lib/netcore50/_._", - "lib/netstandard1.3/System.Threading.Thread.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Threading.Thread.dll", - "ref/netstandard1.3/System.Threading.Thread.dll", - "ref/netstandard1.3/System.Threading.Thread.xml", - "ref/netstandard1.3/de/System.Threading.Thread.xml", - "ref/netstandard1.3/es/System.Threading.Thread.xml", - "ref/netstandard1.3/fr/System.Threading.Thread.xml", - "ref/netstandard1.3/it/System.Threading.Thread.xml", - "ref/netstandard1.3/ja/System.Threading.Thread.xml", - "ref/netstandard1.3/ko/System.Threading.Thread.xml", - "ref/netstandard1.3/ru/System.Threading.Thread.xml", - "ref/netstandard1.3/zh-hans/System.Threading.Thread.xml", - "ref/netstandard1.3/zh-hant/System.Threading.Thread.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.thread.4.3.0.nupkg.sha512", - "system.threading.thread.nuspec" - ] - }, - "System.Threading.ThreadPool/4.3.0": { - "sha512": "RQpA+UpI6Tlpeedk5JStYk2DM/M3i5HqabI/yDbfj1xDu9bIz9kdoquVpHbh/wQjOJaOCbcgRH8iQcAUv8dRWQ==", - "type": "package", - "path": "system.threading.threadpool/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Threading.ThreadPool.dll", - "lib/netcore50/_._", - "lib/netstandard1.3/System.Threading.ThreadPool.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Threading.ThreadPool.dll", - "ref/netstandard1.3/System.Threading.ThreadPool.dll", - "ref/netstandard1.3/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/de/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/es/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/fr/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/it/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/ja/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/ko/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/ru/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/zh-hans/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/zh-hant/System.Threading.ThreadPool.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.threadpool.4.3.0.nupkg.sha512", - "system.threading.threadpool.nuspec" - ] - }, - "System.Threading.Timer/4.3.0": { - "sha512": "1c6OJYt7574mj5ROIWIRlZSBBvV+bEbmmyiHxG9Wd2A2ixToQEa0vkRm7NCOzUywQBai/3lIy0ZAlgvvx6oXOQ==", - "type": "package", - "path": "system.threading.timer/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net451/_._", - "lib/portable-net451+win81+wpa81/_._", - "lib/win81/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net451/_._", - "ref/netcore50/System.Threading.Timer.dll", - "ref/netcore50/System.Threading.Timer.xml", - "ref/netcore50/de/System.Threading.Timer.xml", - "ref/netcore50/es/System.Threading.Timer.xml", - "ref/netcore50/fr/System.Threading.Timer.xml", - "ref/netcore50/it/System.Threading.Timer.xml", - "ref/netcore50/ja/System.Threading.Timer.xml", - "ref/netcore50/ko/System.Threading.Timer.xml", - "ref/netcore50/ru/System.Threading.Timer.xml", - "ref/netcore50/zh-hans/System.Threading.Timer.xml", - "ref/netcore50/zh-hant/System.Threading.Timer.xml", - "ref/netstandard1.2/System.Threading.Timer.dll", - "ref/netstandard1.2/System.Threading.Timer.xml", - "ref/netstandard1.2/de/System.Threading.Timer.xml", - "ref/netstandard1.2/es/System.Threading.Timer.xml", - "ref/netstandard1.2/fr/System.Threading.Timer.xml", - "ref/netstandard1.2/it/System.Threading.Timer.xml", - "ref/netstandard1.2/ja/System.Threading.Timer.xml", - "ref/netstandard1.2/ko/System.Threading.Timer.xml", - "ref/netstandard1.2/ru/System.Threading.Timer.xml", - "ref/netstandard1.2/zh-hans/System.Threading.Timer.xml", - "ref/netstandard1.2/zh-hant/System.Threading.Timer.xml", - "ref/portable-net451+win81+wpa81/_._", - "ref/win81/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.timer.4.3.0.nupkg.sha512", - "system.threading.timer.nuspec" - ] - }, - "System.Xml.ReaderWriter/4.3.0": { - "sha512": "mREBSX+9OeQ/wwbKKApGUxiGivqNsfNLuHwmb+YfDIGg7DSnl7I27oI71g0RSbdZLe+W/gRKu1EYWO//6JDC5g==", - "type": "package", - "path": "system.xml.readerwriter/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.Xml.ReaderWriter.dll", - "lib/netcore50/System.Xml.ReaderWriter.dll", - "lib/netstandard1.3/System.Xml.ReaderWriter.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.Xml.ReaderWriter.dll", - "ref/netcore50/System.Xml.ReaderWriter.dll", - "ref/netcore50/System.Xml.ReaderWriter.xml", - "ref/netcore50/de/System.Xml.ReaderWriter.xml", - "ref/netcore50/es/System.Xml.ReaderWriter.xml", - "ref/netcore50/fr/System.Xml.ReaderWriter.xml", - "ref/netcore50/it/System.Xml.ReaderWriter.xml", - "ref/netcore50/ja/System.Xml.ReaderWriter.xml", - "ref/netcore50/ko/System.Xml.ReaderWriter.xml", - "ref/netcore50/ru/System.Xml.ReaderWriter.xml", - "ref/netcore50/zh-hans/System.Xml.ReaderWriter.xml", - "ref/netcore50/zh-hant/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/System.Xml.ReaderWriter.dll", - "ref/netstandard1.0/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/de/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/es/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/fr/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/it/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/ja/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/ko/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/ru/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/zh-hans/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/zh-hant/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/System.Xml.ReaderWriter.dll", - "ref/netstandard1.3/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/de/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/es/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/fr/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/it/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/ja/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/ko/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/ru/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/zh-hans/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/zh-hant/System.Xml.ReaderWriter.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.xml.readerwriter.4.3.0.nupkg.sha512", - "system.xml.readerwriter.nuspec" - ] - }, - "System.Xml.XDocument/4.3.0": { - "sha512": "wtkjamltryOim1MLmqUQ+4EwQWhaG7mpWEWlHmHYcKBhXpiLFQ9b4NCJbvlLEj6X+WyKQ+6BXPW5iXWTmGsREw==", - "type": "package", - "path": "system.xml.xdocument/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Xml.XDocument.dll", - "lib/netstandard1.3/System.Xml.XDocument.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Xml.XDocument.dll", - "ref/netcore50/System.Xml.XDocument.xml", - "ref/netcore50/de/System.Xml.XDocument.xml", - "ref/netcore50/es/System.Xml.XDocument.xml", - "ref/netcore50/fr/System.Xml.XDocument.xml", - "ref/netcore50/it/System.Xml.XDocument.xml", - "ref/netcore50/ja/System.Xml.XDocument.xml", - "ref/netcore50/ko/System.Xml.XDocument.xml", - "ref/netcore50/ru/System.Xml.XDocument.xml", - "ref/netcore50/zh-hans/System.Xml.XDocument.xml", - "ref/netcore50/zh-hant/System.Xml.XDocument.xml", - "ref/netstandard1.0/System.Xml.XDocument.dll", - "ref/netstandard1.0/System.Xml.XDocument.xml", - "ref/netstandard1.0/de/System.Xml.XDocument.xml", - "ref/netstandard1.0/es/System.Xml.XDocument.xml", - "ref/netstandard1.0/fr/System.Xml.XDocument.xml", - "ref/netstandard1.0/it/System.Xml.XDocument.xml", - "ref/netstandard1.0/ja/System.Xml.XDocument.xml", - "ref/netstandard1.0/ko/System.Xml.XDocument.xml", - "ref/netstandard1.0/ru/System.Xml.XDocument.xml", - "ref/netstandard1.0/zh-hans/System.Xml.XDocument.xml", - "ref/netstandard1.0/zh-hant/System.Xml.XDocument.xml", - "ref/netstandard1.3/System.Xml.XDocument.dll", - "ref/netstandard1.3/System.Xml.XDocument.xml", - "ref/netstandard1.3/de/System.Xml.XDocument.xml", - "ref/netstandard1.3/es/System.Xml.XDocument.xml", - "ref/netstandard1.3/fr/System.Xml.XDocument.xml", - "ref/netstandard1.3/it/System.Xml.XDocument.xml", - "ref/netstandard1.3/ja/System.Xml.XDocument.xml", - "ref/netstandard1.3/ko/System.Xml.XDocument.xml", - "ref/netstandard1.3/ru/System.Xml.XDocument.xml", - "ref/netstandard1.3/zh-hans/System.Xml.XDocument.xml", - "ref/netstandard1.3/zh-hant/System.Xml.XDocument.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.xml.xdocument.4.3.0.nupkg.sha512", - "system.xml.xdocument.nuspec" - ] - }, - "Tmds.LibC/0.2.0": { - "sha512": "+RvLuNHOLW7cxzgDe9yHLoayBgjsuH2/gJtJnuVMxweKrxxYT6TwQNAmt06SFWpjwk68aRcwwD4FfMMA6tZvVA==", - "type": "package", - "path": "tmds.libc/0.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ref/netstandard2.0/Tmds.LibC.dll", - "runtimes/linux-arm/lib/netstandard2.0/Tmds.LibC.dll", - "runtimes/linux-arm64/lib/netstandard2.0/Tmds.LibC.dll", - "runtimes/linux-x64/lib/netstandard2.0/Tmds.LibC.dll", - "tmds.libc.0.2.0.nupkg.sha512", - "tmds.libc.nuspec" - ] - }, - "Catalyst.Abstractions/0.1.0": { - "type": "project", - "path": "../Catalyst.Abstractions/Catalyst.Abstractions.csproj", - "msbuildProject": "../Catalyst.Abstractions/Catalyst.Abstractions.csproj" - }, - "Catalyst.Protocol/1.0.0": { - "type": "project", - "path": "../Catalyst.Protocol/Catalyst.Protocol.csproj", - "msbuildProject": "../Catalyst.Protocol/Catalyst.Protocol.csproj" - }, - "Nethermind.Core/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj" - }, - "Nethermind.Dirichlet.Numerics/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Dirichlet/Nethermind.Dirichlet.Numerics/Nethermind.Dirichlet.Numerics.csproj", - "msbuildProject": "../../submodules/nethermind/src/Dirichlet/Nethermind.Dirichlet.Numerics/Nethermind.Dirichlet.Numerics.csproj" - }, - "Nethermind.Evm/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj" - }, - "Nethermind.HashLib/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.HashLib/Nethermind.HashLib.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.HashLib/Nethermind.HashLib.csproj" - }, - "Nethermind.Logging/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.Logging/Nethermind.Logging.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.Logging/Nethermind.Logging.csproj" - }, - "Nethermind.Secp256k1/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.Secp256k1/Nethermind.Secp256k1.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.Secp256k1/Nethermind.Secp256k1.csproj" - }, - "Nethermind.Store/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.Store/Nethermind.Store.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.Store/Nethermind.Store.csproj" - } - }, - "projectFileDependencyGroups": { - ".NETCoreApp,Version=v2.2": [ - "Autofac >= 4.8.1", - "Autofac.Configuration >= 4.1.0", - "AutofacSerilogIntegration >= 2.0.0", - "Catalyst.Abstractions >= 0.1.0", - "Dawn.Guard >= 1.9.0", - "DotNetty.Codecs >= 0.6.0", - "DotNetty.Codecs.Protobuf >= 0.6.0", - "DotNetty.Handlers >= 0.6.0", - "Google.Protobuf >= 3.9.1", - "MongoDB.Bson >= 2.7.0", - "Multiformats.Hash >= 1.5.0", - "Nethereum.RLP >= 3.3.0", - "Polly >= 7.1.0", - "Serilog.Enrichers.Environment >= 2.1.3", - "Serilog.Enrichers.Thread >= 3.1.0", - "Serilog.Extensions.Logging >= 2.0.4", - "Serilog.Settings.Configuration >= 3.1.0", - "Serilog.Sinks.Console >= 3.1.1", - "Serilog.Sinks.File >= 4.0.0", - "SharpRepository.Ioc.Autofac >= 2.0.4.2", - "SharpRepository.XmlRepository >= 2.0.1-alpha3", - "SimpleBase >= 1.3.1" - ] - }, - "packageFolders": { - "/home/nsh/.nuget/packages/": {} - }, - "project": { - "version": "1.0.0", - "restore": { - "projectUniqueName": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj", - "projectName": "Catalyst.Core.Lib", - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj", - "packagesPath": "/home/nsh/.nuget/packages/", - "outputPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Lib/", - "projectStyle": "PackageReference", - "configFilePaths": [ - "/home/nsh/RiderProjects/Catalyst.Node/src/nuget.config", - "/home/nsh/.config/NuGet/NuGet.Config" - ], - "originalTargetFrameworks": [ - "netcoreapp2.2" - ], - "sources": { - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "netcoreapp2.2": { - "projectReferences": { - "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj": { - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj" - } - } - } - } - }, - "frameworks": { - "netcoreapp2.2": { - "dependencies": { - "Autofac": { - "target": "Package", - "version": "[4.8.1, )" - }, - "Autofac.Configuration": { - "target": "Package", - "version": "[4.1.0, )" - }, - "AutofacSerilogIntegration": { - "target": "Package", - "version": "[2.0.0, )" - }, - "Dawn.Guard": { - "target": "Package", - "version": "[1.9.0, )" - }, - "DotNetty.Codecs": { - "target": "Package", - "version": "[0.6.0, )" - }, - "DotNetty.Codecs.Protobuf": { - "target": "Package", - "version": "[0.6.0, )" - }, - "DotNetty.Handlers": { - "target": "Package", - "version": "[0.6.0, )" - }, - "Google.Protobuf": { - "target": "Package", - "version": "[3.9.1, )" - }, - "MongoDB.Bson": { - "target": "Package", - "version": "[2.7.0, )" - }, - "Multiformats.Hash": { - "target": "Package", - "version": "[1.5.0, )" - }, - "Nethereum.RLP": { - "target": "Package", - "version": "[3.3.0, )" - }, - "Polly": { - "target": "Package", - "version": "[7.1.0, )" - }, - "Serilog.Enrichers.Environment": { - "target": "Package", - "version": "[2.1.3, )" - }, - "Serilog.Enrichers.Thread": { - "target": "Package", - "version": "[3.1.0, )" - }, - "Serilog.Extensions.Logging": { - "target": "Package", - "version": "[2.0.4, )" - }, - "Serilog.Settings.Configuration": { - "target": "Package", - "version": "[3.1.0, )" - }, - "Serilog.Sinks.Console": { - "target": "Package", - "version": "[3.1.1, )" - }, - "Serilog.Sinks.File": { - "target": "Package", - "version": "[4.0.0, )" - }, - "SharpRepository.Ioc.Autofac": { - "target": "Package", - "version": "[2.0.4.2, )" - }, - "SharpRepository.XmlRepository": { - "target": "Package", - "version": "[2.0.1-alpha3, )" - }, - "SimpleBase": { - "target": "Package", - "version": "[1.3.1, )" - } - } - } - } - } -} \ No newline at end of file diff --git a/src/Catalyst.Core.Lib/project.packagespec.json b/src/Catalyst.Core.Lib/project.packagespec.json deleted file mode 100644 index a563765d28..0000000000 --- a/src/Catalyst.Core.Lib/project.packagespec.json +++ /dev/null @@ -1,115 +0,0 @@ -{ - "version": "1.0.0", - "restore": { - "projectUniqueName": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj", - "projectName": "Catalyst.Core.Lib", - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj", - "outputPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Lib/", - "projectStyle": "PackageReference", - "originalTargetFrameworks": [ - "netcoreapp2.2" - ], - "sources": { - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "netcoreapp2.2": { - "projectReferences": { - "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj": { - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj" - } - } - } - } - }, - "frameworks": { - "netcoreapp2.2": { - "dependencies": { - "Autofac": { - "target": "Package", - "version": "[4.8.1, )" - }, - "Autofac.Configuration": { - "target": "Package", - "version": "[4.1.0, )" - }, - "AutofacSerilogIntegration": { - "target": "Package", - "version": "[2.0.0, )" - }, - "Dawn.Guard": { - "target": "Package", - "version": "[1.9.0, )" - }, - "DotNetty.Codecs": { - "target": "Package", - "version": "[0.6.0, )" - }, - "DotNetty.Codecs.Protobuf": { - "target": "Package", - "version": "[0.6.0, )" - }, - "DotNetty.Handlers": { - "target": "Package", - "version": "[0.6.0, )" - }, - "Google.Protobuf": { - "target": "Package", - "version": "[3.9.1, )" - }, - "MongoDB.Bson": { - "target": "Package", - "version": "[2.7.0, )" - }, - "Multiformats.Hash": { - "target": "Package", - "version": "[1.5.0, )" - }, - "Nethereum.RLP": { - "target": "Package", - "version": "[3.3.0, )" - }, - "Polly": { - "target": "Package", - "version": "[7.1.0, )" - }, - "Serilog.Enrichers.Environment": { - "target": "Package", - "version": "[2.1.3, )" - }, - "Serilog.Enrichers.Thread": { - "target": "Package", - "version": "[3.1.0, )" - }, - "Serilog.Extensions.Logging": { - "target": "Package", - "version": "[2.0.4, )" - }, - "Serilog.Settings.Configuration": { - "target": "Package", - "version": "[3.1.0, )" - }, - "Serilog.Sinks.Console": { - "target": "Package", - "version": "[3.1.1, )" - }, - "Serilog.Sinks.File": { - "target": "Package", - "version": "[4.0.0, )" - }, - "SharpRepository.Ioc.Autofac": { - "target": "Package", - "version": "[2.0.4.2, )" - }, - "SharpRepository.XmlRepository": { - "target": "Package", - "version": "[2.0.1-alpha3, )" - }, - "SimpleBase": { - "target": "Package", - "version": "[1.3.1, )" - } - } - } - } -} \ No newline at end of file diff --git a/src/Catalyst.Core.Modules.Authentication.Tests/Catalyst.Core.Modules.Authentication.Tests.csproj b/src/Catalyst.Core.Modules.Authentication.Tests/Catalyst.Core.Modules.Authentication.Tests.csproj index f89fab13f2..136ba03b87 100644 --- a/src/Catalyst.Core.Modules.Authentication.Tests/Catalyst.Core.Modules.Authentication.Tests.csproj +++ b/src/Catalyst.Core.Modules.Authentication.Tests/Catalyst.Core.Modules.Authentication.Tests.csproj @@ -1,24 +1,31 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Authentication.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Authentication.Tests.snk true + Library + + + + 1701;1702;VSTHRD200;CS8002 - - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + - - + + + + - diff --git a/src/Catalyst.Core.Modules.Authentication.Tests/Repository/AuthenticationRepositoryTests.cs b/src/Catalyst.Core.Modules.Authentication.Tests/Repository/AuthenticationRepositoryTests.cs index 56788ff6a3..6add2fff53 100644 --- a/src/Catalyst.Core.Modules.Authentication.Tests/Repository/AuthenticationRepositoryTests.cs +++ b/src/Catalyst.Core.Modules.Authentication.Tests/Repository/AuthenticationRepositoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,39 +29,39 @@ using Catalyst.TestUtils; using FluentAssertions; using SharpRepository.InMemoryRepository; -using Xunit; +using NUnit.Framework; +using MultiFormats; namespace Catalyst.Core.Modules.Authentication.Tests.Repository { public sealed class AuthenticationRepositoryTests { private readonly IAuthenticationStrategy _repositoryAuthenticationStrategy; - private readonly PeerId _trustedPeer; + private readonly MultiAddress _trustedPeer; public AuthenticationRepositoryTests() { - _trustedPeer = PeerIdHelper.GetPeerId("Trusted"); + _trustedPeer = MultiAddressHelper.GetAddress("Trusted"); var whiteListRepo = new AuthCredentialRepository(new InMemoryRepository()); whiteListRepo.Add(new AuthCredentials { - PublicKey = _trustedPeer.PublicKey.KeyToString(), - IpAddress = _trustedPeer.Ip.ToString() + Address = _trustedPeer.ToString() }); _repositoryAuthenticationStrategy = new RepositoryAuthenticationStrategy(whiteListRepo); } - [Fact] + [Test] public void Can_Validate_Trusted_Peer() { _repositoryAuthenticationStrategy.Authenticate(_trustedPeer).Should().BeTrue(); } - [Fact] + [Test] public void Can_Invalidate_Untrusted_Peer() { - _repositoryAuthenticationStrategy.Authenticate(PeerIdHelper.GetPeerId("NotTrusted")) + _repositoryAuthenticationStrategy.Authenticate(MultiAddressHelper.GetAddress("NotTrusted")) .Should().BeFalse(); } } diff --git a/src/Catalyst.Core.Modules.Authentication.Tests/UnitTests/AuthenticationHandlerTests.cs b/src/Catalyst.Core.Modules.Authentication.Tests/UnitTests/AuthenticationHandlerTests.cs index 5567186d7d..30f4670bb4 100644 --- a/src/Catalyst.Core.Modules.Authentication.Tests/UnitTests/AuthenticationHandlerTests.cs +++ b/src/Catalyst.Core.Modules.Authentication.Tests/UnitTests/AuthenticationHandlerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,51 +24,52 @@ using Catalyst.Abstractions.IO.Handlers; using Catalyst.Abstractions.Rpc.Authentication; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Modules.Cryptography.BulletProofs; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; using Catalyst.Protocol.Rpc.Node; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels.Embedded; +using MultiFormats; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Authentication.Tests.UnitTests { public sealed class AuthenticationHandlerTests { - private readonly IAuthenticationStrategy _authenticationStrategy; - private readonly EmbeddedChannel _serverChannel; - private readonly IObservableServiceHandler _testObservableServiceHandler; - private readonly ProtocolMessage _signedMessage; + private IAuthenticationStrategy _authenticationStrategy; + private EmbeddedChannel _serverChannel; + private IObservableServiceHandler _testObservableServiceHandler; + private ProtocolMessage _signedMessage; - public AuthenticationHandlerTests() + [SetUp] + public void Init() { - _testObservableServiceHandler = Substitute.For(); + _testObservableServiceHandler = Substitute.For>(); _authenticationStrategy = Substitute.For(); _serverChannel = new EmbeddedChannel(new AuthenticationHandler(_authenticationStrategy), _testObservableServiceHandler); - var senderId = PeerIdHelper.GetPeerId("Test"); + var senderAddress = MultiAddressHelper.GetAddress("Test"); _signedMessage = new GetPeerListRequest() - .ToProtocolMessage(senderId) - .ToSignedProtocolMessage(senderId, new byte[new FfiWrapper().SignatureLength]); + .ToProtocolMessage(senderAddress) + .ToSignedProtocolMessage(senderAddress, new byte[new FfiWrapper().SignatureLength]); } - [Fact] + [Test] public void Can_Block_Pipeline_Non_Authorized_Node_Operator() { - _authenticationStrategy.Authenticate(Arg.Any()).Returns(false); + _authenticationStrategy.Authenticate(Arg.Any()).Returns(false); _serverChannel.WriteInbound(_signedMessage); _authenticationStrategy.ReceivedWithAnyArgs(1).Authenticate(null); _testObservableServiceHandler.DidNotReceiveWithAnyArgs().ChannelRead(null, null); } - [Fact] + [Test] public void Can_Continue_Pipeline_On_Authorized_Node_Operator() { - _authenticationStrategy.Authenticate(Arg.Any()).Returns(true); + _authenticationStrategy.Authenticate(Arg.Any()).Returns(true); _serverChannel.WriteInbound(_signedMessage); _authenticationStrategy.ReceivedWithAnyArgs(1).Authenticate(null); diff --git a/src/Catalyst.Core.Modules.Authentication/AuthenticationModule.cs b/src/Catalyst.Core.Modules.Authentication/AuthenticationModule.cs index a65afaf7d1..26e85f0377 100644 --- a/src/Catalyst.Core.Modules.Authentication/AuthenticationModule.cs +++ b/src/Catalyst.Core.Modules.Authentication/AuthenticationModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Authentication/Catalyst.Core.Modules.Authentication.csproj b/src/Catalyst.Core.Modules.Authentication/Catalyst.Core.Modules.Authentication.csproj index 463cbd7d4a..a3697d539a 100644 --- a/src/Catalyst.Core.Modules.Authentication/Catalyst.Core.Modules.Authentication.csproj +++ b/src/Catalyst.Core.Modules.Authentication/Catalyst.Core.Modules.Authentication.csproj @@ -1,17 +1,17 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Authentication - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Authentication.snk true + + 1701;1702;CS8002 + - - - diff --git a/src/Catalyst.Core.Modules.Authentication/Models/AuthCredentials.cs b/src/Catalyst.Core.Modules.Authentication/Models/AuthCredentials.cs index 18ee816a35..a9c2924a0a 100644 --- a/src/Catalyst.Core.Modules.Authentication/Models/AuthCredentials.cs +++ b/src/Catalyst.Core.Modules.Authentication/Models/AuthCredentials.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,8 +25,8 @@ using System.Text; using Catalyst.Abstractions.Attributes; using Catalyst.Abstractions.Rpc.Authentication; +using Catalyst.Abstractions.Service.Attributes; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Repository.Attributes; using Newtonsoft.Json; using SharpRepository.Repository; @@ -38,13 +38,9 @@ namespace Catalyst.Core.Modules.Authentication.Models [Audit] public class AuthCredentials : IAuthCredentials { - /// Gets or sets the public key. - /// The public key. - public string PublicKey { get; set; } - - /// Gets or sets the ip address. - /// The ip address. - public string IpAddress { get; set; } + /// Gets or sets the address. + /// The multi address. + public string Address { get; set; } /// public DateTime Created { get; set; } @@ -54,6 +50,6 @@ public class AuthCredentials : IAuthCredentials [RepositoryPrimaryKey(Order = 1)] [JsonProperty("id")] - public string DocumentId => Encoding.UTF8.GetBytes($"{PublicKey}:{IpAddress}").ToByteString().ToBase64(); + public string DocumentId => Encoding.UTF8.GetBytes($"{Address}").ToByteString().ToBase64(); } } diff --git a/src/Catalyst.Core.Modules.Authentication/NoAuthenticationStrategy.cs b/src/Catalyst.Core.Modules.Authentication/NoAuthenticationStrategy.cs index 58efa2cd91..00fc31f398 100644 --- a/src/Catalyst.Core.Modules.Authentication/NoAuthenticationStrategy.cs +++ b/src/Catalyst.Core.Modules.Authentication/NoAuthenticationStrategy.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,6 +23,7 @@ using Catalyst.Abstractions.Rpc.Authentication; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Core.Modules.Authentication { @@ -33,6 +34,6 @@ namespace Catalyst.Core.Modules.Authentication public class NoAuthenticationStrategy : IAuthenticationStrategy { /// - public bool Authenticate(PeerId peerIdentifier) { return true; } + public bool Authenticate(MultiAddress Addressentifier) { return true; } } } diff --git a/src/Catalyst.Core.Modules.Authentication/Repository/AuthCredentialRepository.cs b/src/Catalyst.Core.Modules.Authentication/Repository/AuthCredentialRepository.cs index f89bc02bd5..1f5bfee560 100644 --- a/src/Catalyst.Core.Modules.Authentication/Repository/AuthCredentialRepository.cs +++ b/src/Catalyst.Core.Modules.Authentication/Repository/AuthCredentialRepository.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,14 +21,34 @@ #endregion -using Catalyst.Core.Lib.Repository; using Catalyst.Core.Modules.Authentication.Models; +using MultiFormats; using SharpRepository.Repository; namespace Catalyst.Core.Modules.Authentication.Repository { - public sealed class AuthCredentialRepository : RepositoryWrapper, IAuthCredentialRepository + public sealed class AuthCredentialRepository : IAuthCredentialRepository { - public AuthCredentialRepository(IRepository repository) : base(repository) { } + private readonly IRepository _repository; + + public AuthCredentialRepository(IRepository repository) + { + _repository = repository; + } + + public void Add(AuthCredentials authCredentials) + { + _repository.Add(authCredentials); + } + + public bool TryFind(MultiAddress address, out AuthCredentials authCredentials) + { + return _repository.TryFind(t => t.Address == address.ToString(), out authCredentials); + } + + public void Dispose() + { + _repository.Dispose(); + } } } diff --git a/src/Catalyst.Core.Modules.Authentication/Repository/IAuthCredentialRepository.cs b/src/Catalyst.Core.Modules.Authentication/Repository/IAuthCredentialRepository.cs index 76300d64aa..5f088f0b05 100644 --- a/src/Catalyst.Core.Modules.Authentication/Repository/IAuthCredentialRepository.cs +++ b/src/Catalyst.Core.Modules.Authentication/Repository/IAuthCredentialRepository.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,16 @@ #endregion -using Catalyst.Abstractions.Repository; using Catalyst.Core.Modules.Authentication.Models; +using Catalyst.Protocol.Peer; +using MultiFormats; +using System; namespace Catalyst.Core.Modules.Authentication.Repository { - public interface IAuthCredentialRepository : IRepositoryWrapper { } + public interface IAuthCredentialRepository : IDisposable + { + void Add(AuthCredentials authCredentials); + bool TryFind(MultiAddress Addressentifier, out AuthCredentials authCredentials); + } } diff --git a/src/Catalyst.Core.Modules.Authentication/RepositoryAuthenticationStrategy.cs b/src/Catalyst.Core.Modules.Authentication/RepositoryAuthenticationStrategy.cs index c616bc4c9e..666871fb30 100644 --- a/src/Catalyst.Core.Modules.Authentication/RepositoryAuthenticationStrategy.cs +++ b/src/Catalyst.Core.Modules.Authentication/RepositoryAuthenticationStrategy.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,11 +21,10 @@ #endregion -using System.Linq; using Catalyst.Abstractions.Rpc.Authentication; -using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Authentication.Repository; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Core.Modules.Authentication { @@ -46,10 +45,9 @@ public RepositoryAuthenticationStrategy(IAuthCredentialRepository trustedPeers) } /// - public bool Authenticate(PeerId peerIdentifier) + public bool Authenticate(MultiAddress address) { - return _trustedPeers.TryFind(t => t.IpAddress.Equals(peerIdentifier.Ip.ToString()) && - t.PublicKey.KeyToBytes().SequenceEqual(peerIdentifier.PublicKey), out _); + return _trustedPeers.TryFind(address, out _); } } } diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/Catalyst.Core.Modules.Consensus.Tests.csproj b/src/Catalyst.Core.Modules.Consensus.Tests/Catalyst.Core.Modules.Consensus.Tests.csproj index 589cfc2bcb..0ff43f5b0e 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/Catalyst.Core.Modules.Consensus.Tests.csproj +++ b/src/Catalyst.Core.Modules.Consensus.Tests/Catalyst.Core.Modules.Consensus.Tests.csproj @@ -1,16 +1,27 @@ - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Consensus.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Consensus.Tests.snk true + + 1701;1702;VSTHRD200;CS8002 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/ConsensusTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/ConsensusTests.cs index 0dcdfc5f98..83f92173b1 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/ConsensusTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/ConsensusTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,34 +24,38 @@ using System; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Ledger; +using Catalyst.Core.Abstractions.Sync; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Consensus.Cycle; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Protocol.Deltas; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; -using LibP2P; +using Lib.P2P; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests { public class ConsensusTests : IDisposable { - private readonly IHashProvider _hashProvider; - private readonly IDeltaBuilder _deltaBuilder; - private readonly IDeltaVoter _deltaVoter; - private readonly IDeltaElector _deltaElector; - private readonly IDeltaCache _deltaCache; - private readonly IDeltaHub _deltaHub; - private readonly TestCycleEventProvider _cycleEventProvider; - private readonly Consensus _consensus; - - public ConsensusTests() + private IHashProvider _hashProvider; + private IDeltaBuilder _deltaBuilder; + private IDeltaVoter _deltaVoter; + private IDeltaElector _deltaElector; + private IDeltaCache _deltaCache; + private IDeltaHub _deltaHub; + private TestCycleEventProvider _cycleEventProvider; + private Consensus _consensus; + + [SetUp] + public void Init() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); _cycleEventProvider = new TestCycleEventProvider(); _deltaBuilder = Substitute.For(); _deltaVoter = Substitute.For(); @@ -75,7 +79,7 @@ public ConsensusTests() private Cid PreviousDeltaBytes => _cycleEventProvider.CurrentPhase.PreviousDeltaDfsHash; - [Fact] + [Test] public void ConstructionProducingSubscription_Should_Trigger_BuildDeltaCandidate_On_Construction_Producing_Phase() { @@ -89,7 +93,7 @@ public void _deltaHub.Received(1).BroadcastCandidate(Arg.Is(builtCandidate)); } - [Fact] + [Test] public void CampaigningProductionSubscription_Should_Trigger_TryGetFavouriteDelta_On_Campaigning_Producing_Phase() { @@ -109,7 +113,7 @@ public void _deltaHub.Received(1).BroadcastFavouriteCandidateDelta(Arg.Is(favourite)); } - [Fact] + [Test] public void CampaigningProductionSubscription_Should_Not_Broadcast_On_TryGetFavouriteDelta_Null() { _deltaVoter.TryGetFavouriteDelta(Arg.Any(), out Arg.Any()) @@ -128,7 +132,7 @@ public void CampaigningProductionSubscription_Should_Not_Broadcast_On_TryGetFavo _deltaHub.DidNotReceiveWithAnyArgs().BroadcastFavouriteCandidateDelta(default); } - [Fact] + [Test] public void VotingProductionSubscription_Should_Hit_Cache_And_Publish_To_Dfs() { var popularCandidate = DeltaHelper.GetCandidateDelta(_hashProvider); @@ -145,7 +149,7 @@ public void VotingProductionSubscription_Should_Hit_Cache_And_Publish_To_Dfs() _deltaHub.PublishDeltaToDfsAndBroadcastAddressAsync(default) .ReturnsForAnyArgs( - CidHelper.CreateCid(_hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(1000)))); + _hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(1000)).ToCid()); _cycleEventProvider.MovePastNextPhase(PhaseName.Voting); _cycleEventProvider.Scheduler.Stop(); @@ -157,7 +161,7 @@ public void VotingProductionSubscription_Should_Hit_Cache_And_Publish_To_Dfs() _deltaHub.Received(1)?.PublishDeltaToDfsAndBroadcastAddressAsync(localDelta); } - [Fact] + [Test] public void VotingProductionSubscription_Should_Not_Hit_Cache_Or_Publish_To_Dfs_On_GetMostPopularCandidateDelta_Null() { @@ -174,7 +178,7 @@ public void _deltaHub.DidNotReceiveWithAnyArgs()?.PublishDeltaToDfsAndBroadcastAddressAsync(default); } - [Fact] + [Test] public void VotingProductionSubscription_Should_Not_Publish_To_Dfs_On_GetMostPopularCandidateDelta_Null() { var popularCandidate = DeltaHelper.GetCandidateDelta(_hashProvider); diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleConfigurationTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleConfigurationTests.cs index d002282414..05fab3a4a0 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleConfigurationTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleConfigurationTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,13 +23,13 @@ using Catalyst.Core.Modules.Consensus.Cycle; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Cycle { public class CycleConfigurationTests { - [Fact] + [Test] public void Default_CycleConfiguration_should_not_have_overlapping_phases() { var config = CycleConfiguration.Default; diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleEventsProviderRawTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleEventsProviderRawTests.cs new file mode 100644 index 0000000000..992b270a9f --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleEventsProviderRawTests.cs @@ -0,0 +1,235 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Consensus.Cycle; +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.KeySigner; +using Catalyst.Abstractions.Types; +using Catalyst.Abstractions.Validators; +using Catalyst.Core.Modules.Consensus.Cycle; +using Catalyst.Core.Modules.Cryptography.BulletProofs; +using Catalyst.Core.Modules.Kvm; +using Catalyst.Protocol.Deltas; +using FluentAssertions; +using Lib.P2P; +using Nethermind.Core; +using NSubstitute; +using NUnit.Framework; +using Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Cycle +{ + [TestFixture] + public sealed class CycleEventsProviderRawTests + { + private ICycleEventsProvider _cycleEventsProvider; + private ManualResetEventSlim _manualResetEventSlim; + private IKeySigner _keySigner; + private IValidatorSetStore _validatorSetStore; + private IDeltaCache _deltaCache; + + [SetUp] + public void Init() + { + FfiWrapper cryptoContext = new(); + var privateKey = cryptoContext.GeneratePrivateKey(); + var publicKey = privateKey.GetPublicKey(); + + _keySigner = Substitute.For(); + _keySigner.GetPrivateKey(KeyRegistryTypes.DefaultKey).GetPublicKey().Returns(publicKey); + + _validatorSetStore = Substitute.For(); + _validatorSetStore.Get(Arg.Any()).GetValidators().Returns(new List
{ publicKey.ToKvmAddress() }); + + var deltaNumber = 0; + _deltaCache = Substitute.For(); + _deltaCache.TryGetOrAddConfirmedDelta(Arg.Any(), out Arg.Any()) + .Returns(x => + { + x[1] = new Delta { DeltaNumber = deltaNumber++ }; + return true; + }); + + _cycleEventsProvider = new CycleEventsProviderRaw(CycleConfiguration.Default, new DateTimeProvider(), Substitute.For(), _deltaCache, _validatorSetStore, _keySigner, Substitute.For()); + + //Block until we receive a completed event from the PhaseChanges observable. + _manualResetEventSlim = new ManualResetEventSlim(); + } + + [TearDown] + public void TearDown() + { + _cycleEventsProvider.Dispose(); + _manualResetEventSlim.Dispose(); + } + + [Test] + public async Task Should_Produce_Phases_In_Order() + { + //The cycle event phases in order. + var phasesInOrder = new List<(PhaseName, PhaseStatus)> { + (PhaseName.Construction, PhaseStatus.Producing), + (PhaseName.Construction, PhaseStatus.Collecting), + (PhaseName.Campaigning, PhaseStatus.Producing), + (PhaseName.Campaigning, PhaseStatus.Collecting), + (PhaseName.Voting, PhaseStatus.Producing), + (PhaseName.Voting, PhaseStatus.Collecting), + (PhaseName.Synchronisation, PhaseStatus.Producing), + (PhaseName.Synchronisation, PhaseStatus.Collecting) + }; + + //Start the event cycle. + await _cycleEventsProvider.StartAsync(); + + var fakeObserver = Substitute.For>(); + + //Listen for new phase events. + var phaseChangesDisposable = _cycleEventsProvider.PhaseChanges.Subscribe(fakeObserver); + + Task.Delay(_cycleEventsProvider.Configuration.CycleDuration * 2).Wait(); + + //Check the phase events are in the received and in correct order + Received.InOrder(() => + { + var phases = fakeObserver.ReceivedCalls().Select(c => c.GetArguments()[0]).Cast().ToList(); + for (var i = 0; i < phases.Count(); i++) + { + var phaseOrderIndex = i % phasesInOrder.Count(); + phases[i].Name.Should().Be(phasesInOrder[phaseOrderIndex].Item1); + phases[i].Status.Should().Be(phasesInOrder[phaseOrderIndex].Item2); + } + }); + + phaseChangesDisposable.Dispose(); + } + + [Test] + public async Task StartAsync_Should_Start_CycleEventsProvider() + { + //Should be populated to a construction phase with a producing status. + IPhase phase = null; + + //Listen for new phase events. + var phaseChangesDisposable = _cycleEventsProvider.PhaseChanges.Subscribe(currentPhase => + { + phase = currentPhase; + _manualResetEventSlim.Set(); + }); + + //Start the event cycle. + await _cycleEventsProvider.StartAsync(); + + + //Wait for PhaseChanges to complete else fail if it times out. + if (!_manualResetEventSlim.Wait(CycleConfiguration.Default.CycleDuration)) + { + Assert.Fail(); + } + + //PhaseStatus should be a construction phase with a producing status. + phase.Name.Should().Be(PhaseName.Construction); + phase.Status.Should().Be(PhaseStatus.Producing); + + phaseChangesDisposable.Dispose(); + } + + [Test] + public async Task Close_Should_Stop_CycleEventsProvider() + { + //Listen for completed event + var phaseChangesDisposable = _cycleEventsProvider.PhaseChanges.Subscribe(currentPhase => { }, () => + { + _manualResetEventSlim.Set(); + }); + + //Start the event cycle. + await _cycleEventsProvider.StartAsync(); + + //Stop the event cycle provider. + _cycleEventsProvider.Close(); + + //Wait for PhaseChanges to complete else fail if it times out. + if (!_manualResetEventSlim.Wait(CycleConfiguration.Default.CycleDuration)) + { + Assert.Fail(); + } + + phaseChangesDisposable.Dispose(); + } + + [Test] + public void PhaseChanges_Should_Be_Synchronised_Across_Instances() + { + //Offset to start the second event cycle provider at. + var secondProviderStartOffset = CycleConfiguration.Default.CycleDuration.Divide(3); + + //Create a second event cycle provider + using CycleEventsProviderRaw cycleEventsProvider2 = new(CycleConfiguration.Default, new DateTimeProvider(), Substitute.For(), _deltaCache, _validatorSetStore, _keySigner, Substitute.For()); + + //Create event observers for the cycle event providers + var fakeObserver1 = Substitute.For>(); + var fakeObserver2 = Substitute.For>(); + + //Listen for new phase events. + var phaseChangesDisposable1 = _cycleEventsProvider.PhaseChanges.Subscribe(fakeObserver1); + var phaseChangesDisposable2 = cycleEventsProvider2.PhaseChanges.Subscribe(fakeObserver2); + + //Start first event provider. + _cycleEventsProvider.StartAsync(); + + //Delay the second event provider. + Task.Delay(secondProviderStartOffset).Wait(); + + //Start second event provider. + cycleEventsProvider2.StartAsync(); + + //Order all the received phases and only compare the same phases. + Received.InOrder(() => + { + var cycleEventsProviderPhases1 = fakeObserver1.ReceivedCalls().Select(c => c.GetArguments()[0]).Cast().ToList(); + var cycleEventsProviderPhases2 = fakeObserver2.ReceivedCalls().Select(c => c.GetArguments()[0]).Cast().ToList(); + + var callsOffset = cycleEventsProviderPhases1.Count() - cycleEventsProviderPhases2.Count(); + + for (var i = 0; i < cycleEventsProviderPhases2.Count(); i++) + { + var phase1 = cycleEventsProviderPhases1[i + callsOffset]; + var phase2 = cycleEventsProviderPhases2[i]; + + phase1.Name.Should().Be(phase2.Name); + phase1.Status.Should().Be(phase2.Status); + (phase1.UtcStartTime - phase2.UtcStartTime).TotalMilliseconds.Should().BeApproximately(0, 0.0001d, "phases should be in sync"); + } + }); + + phaseChangesDisposable1.Dispose(); + phaseChangesDisposable2.Dispose(); + } + } +} diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleEventsProviderTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleEventsProviderTests.cs index 9c8a2fa59b..d49ebfb3d9 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleEventsProviderTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleEventsProviderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -35,11 +35,11 @@ using Catalyst.Core.Modules.Hashing; using FluentAssertions; using Microsoft.Reactive.Testing; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; + namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Cycle { @@ -50,22 +50,23 @@ public sealed class CycleEventsProviderTests : IDisposable private static readonly PhaseStatus[] StatusesInOrder = {PhaseStatus.Producing, PhaseStatus.Collecting, PhaseStatus.Idle}; - private readonly TestScheduler _testScheduler; - private readonly CycleEventsProvider _cycleProvider; - private readonly IDisposable _subscription; - private readonly IObserver _spy; - private readonly IDateTimeProvider _dateTimeProvider; - private readonly ICycleSchedulerProvider _schedulerProvider; - private readonly ITestOutputHelper _output; - private readonly IStopwatch _stopWatch; - private readonly IDeltaHashProvider _deltaHashProvider; - private readonly ILogger _logger; - - public CycleEventsProviderTests(ITestOutputHelper output) + private TestScheduler _testScheduler; + private CycleEventsProvider _cycleProvider; + private IDisposable _subscription; + private IObserver _spy; + private IDateTimeProvider _dateTimeProvider; + private ICycleSchedulerProvider _schedulerProvider; + private TestContext _output; + private IStopwatch _stopWatch; + private IDeltaHashProvider _deltaHashProvider; + private ILogger _logger; + + [SetUp] + public void Init() { - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + HashProvider hashProvider = new(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); - _output = output; + _output = TestContext.CurrentContext; _testScheduler = new TestScheduler(); _schedulerProvider = Substitute.For(); @@ -81,6 +82,8 @@ public CycleEventsProviderTests(ITestOutputHelper output) _cycleProvider = new CycleEventsProvider(CycleConfiguration.Default, _dateTimeProvider, _schedulerProvider, _deltaHashProvider, _logger); + _cycleProvider.StartAsync().Wait(); + _spy = Substitute.For>(); _stopWatch = _testScheduler.StartStopwatch(); @@ -88,16 +91,16 @@ public CycleEventsProviderTests(ITestOutputHelper output) _subscription = _cycleProvider.PhaseChanges.Take(50) .Subscribe(p => { - _output.WriteLine($"{_stopWatch.Elapsed.TotalSeconds} -- {p}"); + TestContext.WriteLine($"{_stopWatch.Elapsed.TotalSeconds} -- {p}"); _spy.OnNext(p); }, () => { - _output.WriteLine($"completed after {_stopWatch.Elapsed.TotalSeconds:g}"); + TestContext.WriteLine($"completed after {_stopWatch.Elapsed.TotalSeconds:g}"); _spy.OnCompleted(); }); } - [Fact] + [Test] public void PhaseChanges_Should_Complete_When_Stop_Is_Called() { var cancellationTime = CycleConfiguration.Default.CycleDuration @@ -114,7 +117,7 @@ public void PhaseChanges_Should_Complete_When_Stop_Is_Called() "OnNext, and the start of the second loop is 1."); } - [Fact] + [Test] public void Changes_Should_Happen_In_Time() { _testScheduler.Start(); @@ -170,7 +173,7 @@ private void CheckStatusChangesHappenedInOrder(IList phases, DateTime ev } } - [Fact] + [Test] public void PhaseChanges_Should_Be_Synchronised_Across_Instances() { var secondProviderStartOffset = CycleConfiguration.Default.CycleDuration.Divide(3); @@ -178,46 +181,50 @@ public void PhaseChanges_Should_Be_Synchronised_Across_Instances() _testScheduler.AdvanceBy(secondProviderStartOffset.Ticks); var spy2 = Substitute.For>(); - using (var cycleProvider2 = new CycleEventsProvider(CycleConfiguration.Default, _dateTimeProvider, + using (CycleEventsProvider cycleProvider2 = new(CycleConfiguration.Default, _dateTimeProvider, _schedulerProvider, _deltaHashProvider, _logger)) - using (cycleProvider2.PhaseChanges.Take(50 - PhaseCountPerCycle) - .Subscribe(p => - { - _output.WriteLine( - $"{_stopWatch.Elapsed.TotalSeconds.ToString(CultureInfo.InvariantCulture)} % 2 -- {p}"); - spy2.OnNext(p); - }, () => - { - _output.WriteLine( - - // ReSharper disable once InterpolatedStringExpressionIsNotIFormattable - $"% 2 -- completed after {_stopWatch.Elapsed.TotalSeconds.ToString(CultureInfo.InvariantCulture):g}"); - spy2.OnCompleted(); - })) { - _testScheduler.Start(); - - _spy.Received(1).OnCompleted(); - spy2.Received(1).OnCompleted(); - - var receivedPhases = GetReceivedPhases(_spy); - receivedPhases.Count.Should().Be(50); - - var receivedPhases2 = GetReceivedPhases(spy2); - receivedPhases2.Count.Should().Be(50 - PhaseCountPerCycle); - - (receivedPhases2.First().UtcStartTime - receivedPhases.First().UtcStartTime) - .TotalMilliseconds.Should().BeApproximately( - CycleConfiguration.Default.CycleDuration.TotalMilliseconds, 0.0001d, - "the provider should start on the second cycle"); - - foreach (var phases in receivedPhases.Skip(PhaseCountPerCycle) - .Zip(receivedPhases2, (a, b) => new Tuple(a, b))) + cycleProvider2.StartAsync().Wait(); + + using (cycleProvider2.PhaseChanges.Take(50 - PhaseCountPerCycle) + .Subscribe(p => + { + TestContext.WriteLine( + $"{_stopWatch.Elapsed.TotalSeconds.ToString(CultureInfo.InvariantCulture)} % 2 -- {p}"); + spy2.OnNext(p); + }, () => + { + TestContext.WriteLine( + + // ReSharper disable once InterpolatedStringExpressionIsNotIFormattable + $"% 2 -- completed after {_stopWatch.Elapsed.TotalSeconds.ToString(CultureInfo.InvariantCulture):g}"); + spy2.OnCompleted(); + })) { - (phases.Item1.UtcStartTime - phases.Item2.UtcStartTime).TotalMilliseconds - .Should().BeApproximately(0, 0.0001d, "phases should be in sync"); - phases.Item1.Name.Should().Be(phases.Item2.Name); - phases.Item1.Status.Should().Be(phases.Item1.Status); + _testScheduler.Start(); + + _spy.Received(1).OnCompleted(); + spy2.Received(1).OnCompleted(); + + var receivedPhases = GetReceivedPhases(_spy); + receivedPhases.Count.Should().Be(50); + + var receivedPhases2 = GetReceivedPhases(spy2); + receivedPhases2.Count.Should().Be(50 - PhaseCountPerCycle); + + (receivedPhases2.First().UtcStartTime - receivedPhases.First().UtcStartTime) + .TotalMilliseconds.Should().BeApproximately( + CycleConfiguration.Default.CycleDuration.TotalMilliseconds, 0.0002d, + "the provider should start on the second cycle"); + + foreach (var phases in receivedPhases.Skip(PhaseCountPerCycle) + .Zip(receivedPhases2, (a, b) => new Tuple(a, b))) + { + (phases.Item1.UtcStartTime - phases.Item2.UtcStartTime).TotalMilliseconds + .Should().BeApproximately(0, 0.0001d, "phases should be in sync"); + phases.Item1.Name.Should().Be(phases.Item2.Name); + phases.Item1.Status.Should().Be(phases.Item1.Status); + } } } } diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleSchedulerProviderTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleSchedulerProviderTests.cs index 1c075e12ae..f767221d95 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleSchedulerProviderTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Cycle/CycleSchedulerProviderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,16 +24,16 @@ using System.Reactive.Concurrency; using Catalyst.Core.Modules.Consensus.Cycle; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Cycle { public sealed class CycleSchedulerProviderTests { - [Fact] + [Test] public void Scheduler_Should_Be_The_Default_TaskPoolScheduler() { - var provider = new CycleSchedulerProvider(); + CycleSchedulerProvider provider = new(); provider.Scheduler.Should().Be(TaskPoolScheduler.Default); } } diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/DateTimeProviderTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/DateTimeProviderTests.cs index 654f3cd2b1..36209c2f5c 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/DateTimeProviderTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/DateTimeProviderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,16 +23,16 @@ using System; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests { public class DateTimeProviderTests { - [Fact] + [Test] public void UtcNow_should_return_utc_DateTimeKind() { - var provider = new DateTimeProvider(); + DateTimeProvider provider = new(); var now = provider.UtcNow; now.Kind.Should().Be(DateTimeKind.Utc); diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaBuilderTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaBuilderTests.cs index 51f43375a8..64a69994fa 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaBuilderTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaBuilderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,82 +28,140 @@ using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Kvm; using Catalyst.Abstractions.P2P; using Catalyst.Core.Lib.Cryptography; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Extensions.Protocol.Wire; using Catalyst.Core.Lib.Util; -using Catalyst.Core.Modules.Consensus.Deltas; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Core.Modules.Kvm; using Catalyst.Protocol.Deltas; -using Catalyst.Protocol.Peer; +using Catalyst.Core.Modules.Consensus.Deltas.Building; +using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.Protocol.Transaction; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using FluentAssertions; using Google.Protobuf; -using LibP2P; +using Lib.P2P; +using MultiFormats.Registry; using Nethereum.Hex.HexConvertors.Extensions; -using Nethermind.Core.Extensions; +using Nethermind.Core.Crypto; +using Nethermind.Core.Specs; +using Nethermind.Db; using Nethermind.Dirichlet.Numerics; +using Nethermind.Logging; +using Nethermind.State; using NSubstitute; -using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; +using ILogger = Serilog.ILogger; +using MultiFormats; +using Catalyst.Abstractions.Config; +using Catalyst.Core.Lib.Config; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Deltas { public sealed class DeltaBuilderTests { private const ulong DeltaGasLimit = 8_000_000; - private readonly IHashProvider _hashProvider; - private readonly IDeterministicRandomFactory _randomFactory; - private readonly Random _random; - private readonly PeerId _producerId; - private readonly Cid _previousDeltaHash; - private readonly CoinbaseEntry _zeroCoinbaseEntry; - private readonly IDeltaCache _cache; - private readonly IDateTimeProvider _dateTimeProvider; - private readonly ILogger _logger; - private readonly IPeerSettings _peerSettings; - - public DeltaBuilderTests() + private IHashProvider _hashProvider; + private IDeterministicRandomFactory _randomFactory; + private Random _random; + private MultiAddress _producer; + private Cid _previousDeltaHash; + private CoinbaseEntry _zeroCoinbaseEntry; + private IDeltaCache _cache; + private IDateTimeProvider _dateTimeProvider; + private IStateProvider _stateProvider; + private IDeltaExecutor _deltaExecutor; + private ICryptoContext _cryptoContext; + private IDeltaConfig _deltaConfig; + private ITransactionConfig _transactionConfig; + private ILogger _logger; + private IPeerSettings _peerSettings; + + [SetUp] + public void Init() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _deltaConfig = new DeltaConfig(); + _transactionConfig = new TransactionConfig(); - _random = new Random(); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + + _random = new Random(1); _randomFactory = Substitute.For(); _randomFactory.GetDeterministicRandomFromSeed(Arg.Any()) .Returns(ci => new IsaacRandom(((byte[]) ci[0]).ToHex())); - _producerId = PeerIdHelper.GetPeerId("producer"); - _peerSettings = _producerId.ToSubstitutedPeerSettings(); + _producer = MultiAddressHelper.GetAddress("producer"); + _peerSettings = _producer.ToSubstitutedPeerSettings(); - _previousDeltaHash = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("previousDelta")); + _previousDeltaHash = _hashProvider.ComputeUtf8MultiHash("previousDelta").ToCid(); _zeroCoinbaseEntry = new CoinbaseEntry { Amount = UInt256.Zero.ToUint256ByteString(), - ReceiverPublicKey = _producerId.PublicKey.ToByteString() + ReceiverKvmAddress = _producer.GetPublicKeyBytes().ToKvmAddressByteString() }; _logger = Substitute.For(); _cache = Substitute.For(); + Delta previousDelta = new(); + previousDelta.StateRoot = ByteString.CopyFrom(Keccak.EmptyTreeHash.Bytes); + _cache.TryGetOrAddConfirmedDelta(Arg.Any(), out Arg.Any()).Returns(x => + { + x[1] = previousDelta; + return true; + }); + _dateTimeProvider = new DateTimeProvider(); + + IDb codeDb = new MemDb(); + ISnapshotableDb stateDb = new StateDb(); + ISpecProvider specProvider = new CatalystSpecProvider(); + _cryptoContext = new FfiWrapper(); + _stateProvider = new StateProvider(stateDb, codeDb, LimboLogs.Instance); + IStorageProvider storageProvider = new StorageProvider(stateDb, _stateProvider, LimboLogs.Instance); + KatVirtualMachine virtualMachine = new(_stateProvider, + storageProvider, + new StateUpdateHashProvider(), + specProvider, + new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")), + new FfiWrapper(), + LimboLogs.Instance); + _deltaExecutor = new DeltaExecutor(specProvider, + _stateProvider, + storageProvider, + virtualMachine, + _cryptoContext, + _logger); + } + + private readonly Dictionary _publicKeys = new(); + + private IPublicKey GetPublicKey(int index) + { + if (!_publicKeys.ContainsKey(index)) + { + IPublicKey key = _cryptoContext.GeneratePrivateKey().GetPublicKey(); + _publicKeys[index] = key; + } + + return _publicKeys[index]; } - [Fact] + [Test] public void BuildDeltaEmptyPoolContent() { var transactionRetriever = Substitute.For(); transactionRetriever.GetMempoolTransactionsByPriority() - .Returns(new List()); + .Returns(new List()); - var deltaBuilder = new DeltaBuilder(transactionRetriever, _randomFactory, _hashProvider, _peerSettings, - _cache, _dateTimeProvider, _logger); + DeltaBuilder deltaBuilder = new(transactionRetriever, _randomFactory, _hashProvider, _peerSettings, + _cache, _dateTimeProvider, _stateProvider, _deltaExecutor, _deltaConfig, _transactionConfig, _logger); var candidate = deltaBuilder.BuildCandidateDelta(_previousDeltaHash); @@ -112,7 +170,7 @@ public void BuildDeltaEmptyPoolContent() _cache.Received(1).AddLocalDelta(Arg.Is(candidate), Arg.Any()); } - [Fact] + [Test] public void BuildDeltaInvalidTransactionsBasedOnLockTime() { var invalidTransactionList = Enumerable.Range(0, 20).Select(i => @@ -121,21 +179,14 @@ public void BuildDeltaInvalidTransactionsBasedOnLockTime() transactionFees: 954, timestamp: 157, signature: i.ToString()); - transaction.ConfidentialEntries.Add(new ConfidentialEntry - { - Base = new BaseEntry - { - ReceiverPublicKey = "this entry makes the transaction invalid".ToUtf8ByteString() - } - }); - return transaction; + return transaction.PublicEntry; }).ToList(); var transactionRetriever = Substitute.For(); transactionRetriever.GetMempoolTransactionsByPriority().Returns(invalidTransactionList); - var deltaBuilder = new DeltaBuilder(transactionRetriever, _randomFactory, _hashProvider, _peerSettings, - _cache, _dateTimeProvider, _logger); + DeltaBuilder deltaBuilder = new(transactionRetriever, _randomFactory, _hashProvider, _peerSettings, + _cache, _dateTimeProvider, _stateProvider, _deltaExecutor, _deltaConfig, _transactionConfig, _logger); var candidate = deltaBuilder.BuildCandidateDelta(_previousDeltaHash); ValidateDeltaCandidate(candidate, _zeroCoinbaseEntry.ToByteArray()); @@ -143,7 +194,7 @@ public void BuildDeltaInvalidTransactionsBasedOnLockTime() _cache.Received(1).AddLocalDelta(Arg.Is(candidate), Arg.Any()); } - [Fact] + [Test] public void BuildDeltaCheckForAccuracy() { var transactions = Enumerable.Range(0, 20).Select(i => @@ -154,25 +205,25 @@ public void BuildDeltaCheckForAccuracy() transactionFees: (ulong) _random.Next(), timestamp: _random.Next(), signature: i.ToString()); - return transaction; + return transaction.PublicEntry; }).ToList(); var transactionRetriever = BuildRetriever(transactions); var selectedTransactions = BuildSelectedTransactions(transactions); - var salt = BitConverter.GetBytes(_randomFactory.GetDeterministicRandomFromSeed(_previousDeltaHash.ToArray()).NextInt()); - - var rawAndSaltedEntriesBySignature = selectedTransactions.SelectMany( - t => t.PublicEntries.Select(e => + var salt = BitConverter.GetBytes(_randomFactory.GetDeterministicRandomFromSeed(_previousDeltaHash.ToArray()) + .NextInt()); + + var rawAndSaltedEntriesBySignature = selectedTransactions.Select(e => + { + var publicEntriesProtoBuff = e; + return new { - var publicEntriesProtoBuff = e; - return new - { - RawEntry = publicEntriesProtoBuff, - SaltedAndHashedEntry = - _hashProvider.ComputeMultiHash(publicEntriesProtoBuff.ToByteArray().Concat(salt)) - }; - })); + RawEntry = publicEntriesProtoBuff, + SaltedAndHashedEntry = + _hashProvider.ComputeMultiHash(publicEntriesProtoBuff.ToByteArray().Concat(salt)) + }; + }); var shuffledEntriesBytes = rawAndSaltedEntriesBySignature .OrderBy(v => v.SaltedAndHashedEntry.ToArray(), ByteUtil.ByteListComparer.Default) @@ -180,9 +231,9 @@ public void BuildDeltaCheckForAccuracy() .ToArray(); var expectedBytesToHash = BuildExpectedBytesToHash(selectedTransactions, shuffledEntriesBytes); - - var deltaBuilder = new DeltaBuilder(transactionRetriever, _randomFactory, _hashProvider, _peerSettings, - _cache, _dateTimeProvider, _logger); + + DeltaBuilder deltaBuilder = new(transactionRetriever, _randomFactory, _hashProvider, _peerSettings, + _cache, _dateTimeProvider, _stateProvider, _deltaExecutor, _deltaConfig, _transactionConfig, _logger); var candidate = deltaBuilder.BuildCandidateDelta(_previousDeltaHash); ValidateDeltaCandidate(candidate, expectedBytesToHash); @@ -190,29 +241,27 @@ public void BuildDeltaCheckForAccuracy() _cache.Received(1).AddLocalDelta(Arg.Is(candidate), Arg.Any()); } - private static IDeltaTransactionRetriever BuildRetriever(List transactions) + private static IDeltaTransactionRetriever BuildRetriever(IList transactions) { - transactions.ForEach(t => t.AfterConstruction()); var transactionRetriever = Substitute.For(); transactionRetriever.GetMempoolTransactionsByPriority().Returns(transactions); return transactionRetriever; } - [Fact] + [Test] public void BuildDeltaWithContractEntries() { var transactions = Enumerable.Range(0, 20).Select(i => { var transaction = TransactionHelper.GetContractTransaction(ByteString.Empty, - UInt256.Zero, + UInt256.Zero, 21000, (20 + i).GFul(), - Bytes.Empty, - receiverPublicKey: i.ToString(), - transactionFees: (ulong) _random.Next(), + null, + null, timestamp: _random.Next(), signature: i.ToString()); - return transaction; + return transaction.PublicEntry; }).ToList(); var transactionRetriever = BuildRetriever(transactions); @@ -221,27 +270,26 @@ public void BuildDeltaWithContractEntries() var salt = BitConverter.GetBytes( _randomFactory.GetDeterministicRandomFromSeed(_previousDeltaHash.ToArray()).NextInt()); - var rawAndSaltedEntriesBySignature = selectedTransactions.SelectMany( - t => t.ContractEntries.Select(e => + var rawAndSaltedEntriesBySignature = selectedTransactions.Select(e => + { + var contractEntriesProtoBuff = e; + return new { - var contractEntriesProtoBuff = e; - return new - { - RawEntry = contractEntriesProtoBuff, - SaltedAndHashedEntry = - _hashProvider.ComputeMultiHash(contractEntriesProtoBuff.ToByteArray().Concat(salt)) - }; - })); - + RawEntry = contractEntriesProtoBuff, + SaltedAndHashedEntry = + _hashProvider.ComputeMultiHash(contractEntriesProtoBuff.ToByteArray().Concat(salt)) + }; + }); + var shuffledEntriesBytes = rawAndSaltedEntriesBySignature .OrderBy(v => v.SaltedAndHashedEntry.ToArray(), ByteUtil.ByteListComparer.Default) .SelectMany(v => v.RawEntry.ToByteArray()) .ToArray(); - + var expectedBytesToHash = BuildExpectedBytesToHash(selectedTransactions, shuffledEntriesBytes); - var deltaBuilder = new DeltaBuilder(transactionRetriever, _randomFactory, _hashProvider, _peerSettings, - _cache, _dateTimeProvider, _logger); + DeltaBuilder deltaBuilder = new(transactionRetriever, _randomFactory, _hashProvider, _peerSettings, + _cache, _dateTimeProvider, _stateProvider, _deltaExecutor, _deltaConfig, _transactionConfig, _logger); var candidate = deltaBuilder.BuildCandidateDelta(_previousDeltaHash); ValidateDeltaCandidate(candidate, expectedBytesToHash); @@ -249,7 +297,7 @@ public void BuildDeltaWithContractEntries() _cache.Received(1).AddLocalDelta(Arg.Is(candidate), Arg.Any()); } - [Fact] + [Test] public void When_contract_entries_exceed_delta_gas_limit_some_entries_are_ignored() { // each entry at 1 million gas @@ -257,35 +305,36 @@ public void When_contract_entries_exceed_delta_gas_limit_some_entries_are_ignore var transactions = Enumerable.Range(0, 20).Select(i => { var transaction = TransactionHelper.GetContractTransaction(ByteString.Empty, - UInt256.Zero, - i > 10 ? (uint) DeltaGasLimit / 8U - 10000U : 70000U, // to test scenarios when both single transaction is ignored and all remaining + UInt256.Zero, + i > 10 + ? (uint) DeltaGasLimit / 8U - 10000U + : 70000U, // to test scenarios when both single transaction is ignored and all remaining (20 + i).GFul(), - Bytes.Empty, - receiverPublicKey: i.ToString(), - transactionFees: (ulong) _random.Next(), + null, + null, timestamp: _random.Next(), signature: i.ToString()); - return transaction; + return transaction.PublicEntry; }).ToList(); var transactionRetriever = BuildRetriever(transactions); - var expectedSelectedTransactions = BuildSelectedTransactions(transactions.Skip(10).Take(1).Union(transactions.Skip(12).Take(8)).ToList()); + var expectedSelectedTransactions = + BuildSelectedTransactions(transactions.Skip(10).Take(1).Union(transactions.Skip(12).Take(8)).ToList()); var salt = BitConverter.GetBytes( _randomFactory.GetDeterministicRandomFromSeed(_previousDeltaHash.ToArray()).NextInt()); - var rawAndSaltedEntriesBySignature = expectedSelectedTransactions.SelectMany( - t => t.ContractEntries.Select(e => + var rawAndSaltedEntriesBySignature = expectedSelectedTransactions.Select(e => + { + var contractEntriesProtoBuff = e; + return new { - var contractEntriesProtoBuff = e; - return new - { - RawEntry = contractEntriesProtoBuff, - SaltedAndHashedEntry = - _hashProvider.ComputeMultiHash(contractEntriesProtoBuff.ToByteArray().Concat(salt)) - }; - })).ToArray(); - + RawEntry = contractEntriesProtoBuff, + SaltedAndHashedEntry = + _hashProvider.ComputeMultiHash(contractEntriesProtoBuff.ToByteArray().Concat(salt)) + }; + }).ToArray(); + var shuffledEntriesBytes = rawAndSaltedEntriesBySignature .OrderBy(v => v.SaltedAndHashedEntry.ToArray(), ByteUtil.ByteListComparer.Default) .SelectMany(v => v.RawEntry.ToByteArray()) @@ -293,47 +342,47 @@ public void When_contract_entries_exceed_delta_gas_limit_some_entries_are_ignore var expectedBytesToHash = BuildExpectedBytesToHash(expectedSelectedTransactions, shuffledEntriesBytes); - var deltaBuilder = new DeltaBuilder(transactionRetriever, _randomFactory, _hashProvider, _peerSettings, - _cache, _dateTimeProvider, _logger); + DeltaBuilder deltaBuilder = new(transactionRetriever, _randomFactory, _hashProvider, _peerSettings, + _cache, _dateTimeProvider, _stateProvider, _deltaExecutor, _deltaConfig, _transactionConfig, _logger); var candidate = deltaBuilder.BuildCandidateDelta(_previousDeltaHash); ValidateDeltaCandidate(candidate, expectedBytesToHash); _cache.Received(1).AddLocalDelta(Arg.Is(candidate), Arg.Any()); } - - private static TransactionBroadcast[] BuildSelectedTransactions(List transactions) + + private static PublicEntry[] BuildSelectedTransactions(IEnumerable transactions) { - var selectedTransactions = transactions.Where(t => (t.IsPublicTransaction || t.IsContractCall || t.IsContractDeployment) && t.HasValidEntries()).ToArray(); - return selectedTransactions; + return transactions.Where(t => t.IsPublicTransaction || t.IsContractCall || t.IsContractDeployment) + .ToArray(); } - private byte[] BuildExpectedBytesToHash(TransactionBroadcast[] selectedTransactions, byte[] shuffledEntriesBytes) + private byte[] BuildExpectedBytesToHash(PublicEntry[] selectedTransactions, byte[] shuffledEntriesBytes) { var signaturesInOrder = selectedTransactions .Select(p => p.Signature.ToByteArray()) .OrderBy(s => s, ByteUtil.ByteListComparer.Default) .SelectMany(b => b) .ToArray(); - + var expectedCoinBase = new CoinbaseEntry { - Amount = selectedTransactions.Sum(t => t.SummedEntryFees()).ToUint256ByteString(), - ReceiverPublicKey = _producerId.PublicKey.ToByteString() + Amount = selectedTransactions.Sum(t => t.GasPrice.ToUInt256() * t.GasLimit).ToUint256ByteString(), + ReceiverKvmAddress = _producer.GetPublicKeyBytes().ToKvmAddressByteString() }; var expectedBytesToHash = shuffledEntriesBytes.Concat(signaturesInOrder) .Concat(expectedCoinBase.ToByteArray()).ToArray(); return expectedBytesToHash; } - + private void ValidateDeltaCandidate(CandidateDeltaBroadcast candidate, byte[] expectedBytesToHash) { candidate.Should().NotBeNull(); - candidate.ProducerId.Should().Be(_producerId); + candidate.Producer.Should().BeEquivalentTo(_producer.GetKvmAddressByteString()); candidate.PreviousDeltaDfsHash.ToByteArray().SequenceEqual(_previousDeltaHash.ToArray()).Should().BeTrue(); - var expectedHash = CidHelper.CreateCid(_hashProvider.ComputeMultiHash(expectedBytesToHash)); + var expectedHash = _hashProvider.ComputeMultiHash(expectedBytesToHash).ToCid(); candidate.Hash.ToByteArray().Should().BeEquivalentTo(expectedHash.ToArray()); } } diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaCacheTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaCacheTests.cs index 07c23edcc8..464771d307 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaCacheTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaCacheTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,33 +25,38 @@ using System.Linq; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Hashing; -using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Consensus.Deltas; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Protocol.Deltas; using Catalyst.TestUtils; using FluentAssertions; -using LibP2P; +using Lib.P2P; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Primitives; +using MultiFormats.Registry; +using Nethermind.Core.Crypto; +using Nethermind.Db; +using Nethermind.State; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; +using Catalyst.Core.Lib.Service; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Deltas { public class DeltaCacheTests { - private readonly IHashProvider _hashProvider; - private readonly IMemoryCache _memoryCache; - private readonly IDeltaDfsReader _dfsReader; - private readonly DeltaCache _deltaCache; - private readonly ILogger _logger; - - public DeltaCacheTests() + private IHashProvider _hashProvider; + private IMemoryCache _memoryCache; + private IDeltaDfsReader _dfsReader; + private DeltaCache _deltaCache; + private ILogger _logger; + + [SetUp] + public void Init() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); _memoryCache = Substitute.For(); _dfsReader = Substitute.For(); _logger = Substitute.For(); @@ -59,21 +64,25 @@ public DeltaCacheTests() var tokenProvider = Substitute.For(); tokenProvider.GetChangeToken().Returns(Substitute.For()); - _deltaCache = new DeltaCache(_hashProvider, _memoryCache, _dfsReader, tokenProvider, _logger); + var storageProvider = Substitute.For(); + var stateProvider = Substitute.For(); + stateProvider.StateRoot.Returns(Keccak.Zero); + + _deltaCache = new DeltaCache(_hashProvider, _memoryCache, _dfsReader, tokenProvider, storageProvider, stateProvider, Substitute.For(), Substitute.For(), Substitute.For(), _logger); } - [Fact] + [Test] public void Genesis_Hash_Should_Be_Always_Present() { _memoryCache.Received().CreateEntry(_deltaCache.GenesisHash); } - [Fact] + [Test] public void TryGetDelta_Should_Not_Hit_The_Dfs_Or_Store_Delta_When_Delta_Is_In_Cache() { _memoryCache.ClearReceivedCalls(); // needed because of the CreateEntry call from the DeltaCache .ctor var deltaFromCache = DeltaHelper.GetDelta(_hashProvider); - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("abc")); + var cid = _hashProvider.ComputeUtf8MultiHash("abc").ToCid(); _memoryCache.TryGetValue(Arg.Is(cid), out Arg.Any()) .Returns(ci => @@ -91,12 +100,12 @@ public void TryGetDelta_Should_Not_Hit_The_Dfs_Or_Store_Delta_When_Delta_Is_In_C _memoryCache.DidNotReceiveWithAnyArgs().CreateEntry(default); } - [Fact] + [Test] public void TryGetDelta_Should_Hit_The_Dfs_When_Delta_Is_Not_In_Cache() { var deltaFromDfs = DeltaHelper.GetDelta(_hashProvider); - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("def")); + var cid = _hashProvider.ComputeUtf8MultiHash("def").ToCid(); ExpectDeltaFromDfsAndNotFromCache(cid, deltaFromDfs); var cacheEntry = Substitute.For(); @@ -112,17 +121,17 @@ public void TryGetDelta_Should_Hit_The_Dfs_When_Delta_Is_Not_In_Cache() _dfsReader.Received(1).TryReadDeltaFromDfs(cid, out Arg.Any()); } - [Fact] + [Test] public void TryGetDelta_Should_Cache_Delta_With_Expiry_Options_When_Delta_Is_Not_In_Cache() { var deltaFromDfs = DeltaHelper.GetDelta(_hashProvider); - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("ijk")); + var cid = _hashProvider.ComputeUtf8MultiHash("ijk").ToCid(); ExpectDeltaFromDfsAndNotFromCache(cid, deltaFromDfs); var cacheEntry = Substitute.For(); - var expirationTokens = new List(); + List expirationTokens = new(); cacheEntry.ExpirationTokens.Returns(expirationTokens); - var expirationCallbacks = new List(); + List expirationCallbacks = new(); cacheEntry.PostEvictionCallbacks.Returns(expirationCallbacks); _memoryCache.CreateEntry(cid).Returns(cacheEntry); diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaDfsReaderTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaDfsReaderTests.cs index d978d83206..a2bd4f4a34 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaDfsReaderTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaDfsReaderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,62 +24,64 @@ using System; using System.IO; using System.Threading; +using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Dfs; using Catalyst.Abstractions.Hashing; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Util; -using Catalyst.Core.Modules.Consensus.Deltas; +using Catalyst.Core.Modules.Dfs; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.TestUtils; using FluentAssertions; using Google.Protobuf; -using LibP2P; +using Lib.P2P; +using MultiFormats.Registry; using NSubstitute; using NSubstitute.ExceptionExtensions; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Deltas { - public class DeltaDfsReaderTests + public sealed class DeltaDfsReaderTests { - private readonly IHashProvider _hashProvider; - private readonly IDfs _dfs; - private readonly ILogger _logger; - private readonly DeltaDfsReader _dfsReader; + private IHashProvider _hashProvider; + private IDfsService _dfsService; + private ILogger _logger; + private IDeltaDfsReader _dfsReader; - public DeltaDfsReaderTests() + [SetUp] + public void Init() { - var hashingAlgorithm = HashingAlgorithm.GetAlgorithmMetadata("blake2b-256"); + var hashingAlgorithm = HashingAlgorithm.GetAlgorithmMetadata("keccak-256"); _hashProvider = new HashProvider(hashingAlgorithm); - _dfs = Substitute.For(); + _dfsService = Substitute.For(); _logger = Substitute.For(); - _dfsReader = new DeltaDfsReader(_dfs, _logger); + _dfsReader = new DeltaDfsReader(_dfsService, _logger); } - [Fact] + [Test] public void TryReadDeltaFromDfs_Should_Return_False_And_Log_When_Hash_Not_Found_On_Dfs() { - var exception = new FileNotFoundException("that hash is not good"); - _dfs.ReadAsync(Arg.Any(), Arg.Any()) + FileNotFoundException exception = new("that hash is not good"); + _dfsService.UnixFsApi.ReadFileAsync(Arg.Any(), Arg.Any()) .Throws(exception); - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("bad hash")); + var cid = _hashProvider.ComputeUtf8MultiHash("bad hash").ToCid(); _dfsReader.TryReadDeltaFromDfs(cid, out _, CancellationToken.None).Should().BeFalse(); _logger.Received(1).Error(exception, Arg.Any(), Arg.Is(s => s == cid)); } - [Fact] + [Test] public void TryReadDeltaFromDfs_Should_Return_True_When_Hash_Found_On_Dfs_And_Delta_Is_Valid() { - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("good hash")); + var cid = _hashProvider.ComputeUtf8MultiHash("good hash").ToCid().ToString(); var matchingDelta = DeltaHelper.GetDelta(_hashProvider); - _dfs.ReadAsync(cid, CancellationToken.None) + _dfsService.UnixFsApi.ReadFileAsync(cid, CancellationToken.None) .Returns(matchingDelta.ToByteArray().ToMemoryStream()); var found = _dfsReader.TryReadDeltaFromDfs(cid, out var delta, CancellationToken.None); @@ -88,17 +90,17 @@ public void TryReadDeltaFromDfs_Should_Return_True_When_Hash_Found_On_Dfs_And_De delta.Should().Be(matchingDelta); } - [Fact] + [Test] public void TryReadDeltaFromDfs_Should_Return_False_When_Hash_Found_On_Dfs_And_Delta_Is_Not_Valid() { - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("good hash")); + var cid = _hashProvider.ComputeUtf8MultiHash("good hash").ToCid(); var matchingDelta = DeltaHelper.GetDelta(_hashProvider); matchingDelta.PreviousDeltaDfsHash = ByteString.Empty; new Action(() => matchingDelta.IsValid()).Should() .Throw("otherwise this test is useless"); - _dfs.ReadAsync(cid, CancellationToken.None) + _dfsService.UnixFsApi.ReadFileAsync(cid, CancellationToken.None) .Returns(matchingDelta.ToByteArray().ToMemoryStream()); var found = _dfsReader.TryReadDeltaFromDfs(cid, out var delta, CancellationToken.None); @@ -107,19 +109,19 @@ public void TryReadDeltaFromDfs_Should_Return_False_When_Hash_Found_On_Dfs_And_D delta.Should().BeNull(); } - [Fact] + [Test] public void TryReadDeltaFromDfs_Should_Pass_Cancellation_Token() { - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("good hash")); - var cancellationToken = new CancellationToken(); + var cid = _hashProvider.ComputeUtf8MultiHash("good hash").ToCid(); + CancellationToken cancellationToken = new(); var matchingDelta = DeltaHelper.GetDelta(_hashProvider); - _dfs.ReadAsync(cid, CancellationToken.None) + _dfsService.UnixFsApi.ReadFileAsync(cid, CancellationToken.None) .Returns(matchingDelta.ToByteArray().ToMemoryStream()); _dfsReader.TryReadDeltaFromDfs(cid, out _, CancellationToken.None); - _dfs.Received(1)?.ReadAsync(Arg.Is(cid), Arg.Is(cancellationToken)); + _dfsService.UnixFsApi.Received(1)?.ReadFileAsync(Arg.Is(cid.ToString()), Arg.Is(cancellationToken)); } } } diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaElectorTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaElectorTests.cs index e83583585a..6bcd10e69b 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaElectorTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaElectorTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,84 +27,121 @@ using System.Linq; using System.Reactive.Linq; using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Extensions; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Core.Lib.P2P.ReputationSystem; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Consensus.Deltas; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using FluentAssertions; -using LibP2P; +using Lib.P2P; using Microsoft.Extensions.Caching.Memory; +using Microsoft.Reactive.Testing; +using MultiFormats.Registry; using NSubstitute; +using NUnit.Framework; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using SharpRepository.InMemoryRepository; +using Peer = Catalyst.Core.Lib.P2P.Models.Peer; +using Catalyst.Core.Lib.P2P.Repository; +using MultiFormats; +using Nethermind.Core; +using Catalyst.Core.Modules.Kvm; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Deltas { - public class BadFavouritesData : TheoryData + public class DeltaElectorTests { - public BadFavouritesData() + private static IHashProvider HashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + + private TestScheduler _testScheduler; + private ILogger _logger; + private IReputationManager _reputationManager; + private IHashProvider _hashProvider; + private IMemoryCache _cache; + private IDeltaProducersProvider _deltaProducersProvider; + + [SetUp] + public void Init() + { + _testScheduler = new TestScheduler(); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + _logger = Substitute.For(); + _reputationManager = + new ReputationManager(new PeerRepository(new InMemoryRepository()), _logger, _testScheduler); + _cache = Substitute.For(); + _deltaProducersProvider = Substitute.For(); + } + + [Test] + public void When_receiving_null_favourite_should_log_and_not_hit_the_cache() + { + DeltaElector elector = new(_cache, _deltaProducersProvider, _reputationManager, _logger); + + elector.OnNext(null); + + _cache.DidNotReceiveWithAnyArgs().TryGetValue(Arg.Any(), out Arg.Any()); + _cache.DidNotReceiveWithAnyArgs().CreateEntry(Arg.Any()); + } + + [Test] + public void When_receiving_empty_favourite_should_log_and_not_hit_the_cache() { - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + DeltaElector elector = new(_cache, _deltaProducersProvider, _reputationManager, _logger); + + elector.OnNext(new FavouriteDeltaBroadcast()); - Add(null, typeof(ArgumentNullException)); - Add(new FavouriteDeltaBroadcast(), typeof(InvalidDataException)); - Add(new FavouriteDeltaBroadcast + _cache.DidNotReceiveWithAnyArgs().TryGetValue(Arg.Any(), out Arg.Any()); + _cache.DidNotReceiveWithAnyArgs().CreateEntry(Arg.Any()); + } + + [Test] + public void When_receiving_invalid_candidate_should_log_and_not_hit_the_cache() + { + DeltaElector elector = new(_cache, _deltaProducersProvider, _reputationManager, _logger); + + elector.OnNext(new FavouriteDeltaBroadcast { Candidate = new CandidateDeltaBroadcast { Hash = ByteUtil.GenerateRandomByteArray(32).ToByteString(), - ProducerId = PeerIdHelper.GetPeerId("unknown_producer") + Producer = MultiAddressHelper.GetAddress("unknown_producer").GetPublicKeyBytes().ToKvmAddress().Bytes.ToByteString() }, - VoterId = PeerIdHelper.GetPeerId("candidate field is invalid") - }, typeof(InvalidDataException)); - Add(new FavouriteDeltaBroadcast - { - Candidate = DeltaHelper.GetCandidateDelta(hashProvider) - }, typeof(InvalidDataException)); - } - } + Voter = MultiAddressHelper.GetAddress("candidate field is invalid").GetPublicKeyBytes().ToKvmAddress().Bytes.ToByteString() + }); - public class DeltaElectorTests - { - private readonly ILogger _logger; - private readonly IHashProvider _hashProvider; - private readonly IMemoryCache _cache; - private readonly IDeltaProducersProvider _deltaProducersProvider; - - public DeltaElectorTests() - { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - _logger = Substitute.For(); - _cache = Substitute.For(); - _deltaProducersProvider = Substitute.For(); + _cache.DidNotReceiveWithAnyArgs().TryGetValue(Arg.Any(), out Arg.Any()); + _cache.DidNotReceiveWithAnyArgs().CreateEntry(Arg.Any()); } - [Theory] - [ClassData(typeof(BadFavouritesData))] - public void When_receiving_bad_favourite_should_log_and_not_hit_the_cache(FavouriteDeltaBroadcast badFavourite, - Type exceptionType) + [Test] + public void When_receiving_no_voterid_should_log_and_not_hit_the_cache() { - var elector = new DeltaElector(_cache, _deltaProducersProvider, _logger); + DeltaElector elector = new(_cache, _deltaProducersProvider, _reputationManager, _logger); - elector.OnNext(badFavourite); + elector.OnNext(new FavouriteDeltaBroadcast + { + Candidate = DeltaHelper.GetCandidateDelta(HashProvider) + }); _cache.DidNotReceiveWithAnyArgs().TryGetValue(Arg.Any(), out Arg.Any()); _cache.DidNotReceiveWithAnyArgs().CreateEntry(Arg.Any()); } - [Fact] + [Test] public void When_receiving_new_valid_favourite_should_store_in_cache() { var favourite = DeltaHelper.GetFavouriteDelta(_hashProvider); var candidateListKey = DeltaElector.GetCandidateListCacheKey(favourite); - AddVoterAsExpectedProducer(favourite.VoterId); + AddVoterAsExpectedProducer(new Address(favourite.Voter.ToByteArray())); - var elector = new DeltaElector(_cache, _deltaProducersProvider, _logger); + DeltaElector elector = new(_cache, _deltaProducersProvider, _reputationManager, _logger); var addedEntry = Substitute.For(); _cache.CreateEntry(Arg.Is(s => s.Equals(candidateListKey))) @@ -117,20 +154,20 @@ public void When_receiving_new_valid_favourite_should_store_in_cache() var addedValue = addedEntry.Value; addedValue.Should().BeAssignableTo>(); - ((IDictionary) addedValue).Should().ContainKey(favourite); + ((IDictionary)addedValue).Should().ContainKey(favourite); } - [Fact] + [Test] public void When_receiving_known_favourite_should_not_store_in_cache() { - using (var realCache = new MemoryCache(new MemoryCacheOptions())) + using (MemoryCache realCache = new(new MemoryCacheOptions())) { var favourite = DeltaHelper.GetFavouriteDelta(_hashProvider); var candidateListKey = DeltaElector.GetCandidateListCacheKey(favourite); - AddVoterAsExpectedProducer(favourite.VoterId); + AddVoterAsExpectedProducer(new Address(favourite.Voter.ToByteArray())); - var elector = new DeltaElector(realCache, _deltaProducersProvider, _logger); + DeltaElector elector = new(realCache, _deltaProducersProvider, _reputationManager, _logger); elector.OnNext(favourite); elector.OnNext(favourite); @@ -143,16 +180,16 @@ public void When_receiving_known_favourite_should_not_store_in_cache() } } - [Fact] + [Test] public void When_voter_not_a_producer_should_not_save_vote() { var favourite = DeltaHelper.GetFavouriteDelta(_hashProvider); _deltaProducersProvider .GetDeltaProducersFromPreviousDelta(Arg.Any()) - .Returns(new List {PeerIdHelper.GetPeerId("the only known producer")}); + .Returns(new List
{ MultiAddressHelper.GetAddress("the only known producer").GetPublicKeyBytes().ToKvmAddress() }); - var elector = new DeltaElector(_cache, _deltaProducersProvider, _logger); + DeltaElector elector = new(_cache, _deltaProducersProvider, _reputationManager, _logger); elector.OnNext(favourite); @@ -162,15 +199,42 @@ public void When_voter_not_a_producer_should_not_save_vote() _cache.DidNotReceiveWithAnyArgs().TryGetValue(default, out _); } - [Fact] + [Test] + public void Should_DeRep_Peers_That_Vote_When_They_Are_Not_Delta_Producers() + { + var peerMultiAddress = MultiAddressHelper.GetAddress("peer"); + var favourite = DeltaHelper.GetFavouriteDelta(_hashProvider, voterId: peerMultiAddress.GetKvmAddress()); + + var peer = new Peer { Reputation = 100, Address = peerMultiAddress, KvmAddress = peerMultiAddress.GetKvmAddress() }; + _reputationManager.PeerRepository.Add(peer); + + var expectedReputation = peer.Reputation + ReputationEventType.VoterIsNotProducer.Amount; + + _deltaProducersProvider + .GetDeltaProducersFromPreviousDelta(Arg.Any()) + .Returns(new List
()); + + DeltaElector elector = new(_cache, _deltaProducersProvider, _reputationManager, _logger); + + elector.OnNext(favourite); + + _testScheduler.Start(); + + var updatedPeer = _reputationManager.PeerRepository.Get(peerMultiAddress.ToString()); + expectedReputation.Should().Be(updatedPeer.Reputation); + + _cache.DidNotReceiveWithAnyArgs().TryGetValue(default, out _); + } + + [Test] public void When_favourite_has_different_producer_it_should_not_create_duplicate_entries() { - using (var realCache = new MemoryCache(new MemoryCacheOptions())) + using (MemoryCache realCache = new(new MemoryCacheOptions())) { - var producers = "abc".Select(c => PeerIdHelper.GetPeerId(c.ToString())) + var producers = "abc".Select(c => MultiAddressHelper.GetAddress(c.ToString()).GetPublicKeyBytes().ToKvmAddress()) .ToArray(); - var hashProduced = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("newHash")); - var previousHash = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("prevHash")); + var hashProduced = _hashProvider.ComputeUtf8MultiHash("newHash").ToCid(); + var previousHash = _hashProvider.ComputeUtf8MultiHash("prevHash").ToCid(); var candidates = producers.Select((p, i) => DeltaHelper.GetCandidateDelta(_hashProvider, previousHash, hashProduced, producers[i]) ).ToArray(); @@ -180,14 +244,14 @@ public void When_favourite_has_different_producer_it_should_not_create_duplicate .Concat(Enumerable.Repeat(candidates[1], 2)) .Concat(Enumerable.Repeat(candidates[2], 8)) .Select((c, j) => new FavouriteDeltaBroadcast - { - Candidate = c, - VoterId = PeerIdHelper.GetPeerId((j % votersCount).ToString()) - }).ToList(); + { + Candidate = c, + Voter = MultiAddressHelper.GetAddress((j % votersCount).ToString()).GetPublicKeyBytes().ToKvmAddressByteString() + }).ToList(); - AddVoterAsExpectedProducer(favourites.Select(f => f.VoterId).ToArray()); + AddVoterAsExpectedProducer(favourites.Select(f => new Address(f.Voter.ToByteArray())).ToArray()); - var elector = new DeltaElector(realCache, _deltaProducersProvider, _logger); + DeltaElector elector = new(realCache, _deltaProducersProvider, _reputationManager, _logger); var favouriteStream = favourites.ToObservable(); using (favouriteStream.Subscribe(elector)) @@ -204,15 +268,15 @@ public void When_favourite_has_different_producer_it_should_not_create_duplicate } } - [Fact] + [Test] public void GetMostPopularCandidateDelta_should_return_the_favourite_with_most_voter_ids() { - using (var realCache = new MemoryCache(new MemoryCacheOptions())) + using (MemoryCache realCache = new(new MemoryCacheOptions())) { - var producers = "ab".Select(c => PeerIdHelper.GetPeerId(c.ToString())) + var producers = "ab".Select(c => MultiAddressHelper.GetAddress(c.ToString()).GetPublicKeyBytes().ToKvmAddress()) .ToArray(); - var previousHash = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("previousHash")); - var newHash = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("newHash")); + var previousHash = _hashProvider.ComputeUtf8MultiHash("previousHash").ToCid(); + var newHash = _hashProvider.ComputeUtf8MultiHash("newHash").ToCid(); var candidates = producers.Select((p, i) => DeltaHelper.GetCandidateDelta(_hashProvider, previousHash, newHash, producers[i]) ).ToArray(); @@ -223,14 +287,14 @@ public void GetMostPopularCandidateDelta_should_return_the_favourite_with_most_v .Concat(Enumerable.Repeat(candidates[1], secondVoteCount)) .Shuffle() .Select((c, j) => new FavouriteDeltaBroadcast - { - Candidate = c, - VoterId = PeerIdHelper.GetPeerId(j.ToString()) - }).ToList(); + { + Candidate = c, + Voter = MultiAddressHelper.GetAddress(j.ToString()).GetPublicKeyBytes().ToKvmAddressByteString() + }).ToList(); - AddVoterAsExpectedProducer(favourites.Select(f => f.VoterId).ToArray()); + AddVoterAsExpectedProducer(favourites.Select(f => new Address(f.Voter.ToByteArray())).ToArray()); - var elector = new DeltaElector(realCache, _deltaProducersProvider, _logger); + DeltaElector elector = new(realCache, _deltaProducersProvider, _reputationManager, _logger); var favouriteStream = favourites.ToObservable(); using (favouriteStream.Subscribe(elector)) @@ -246,14 +310,14 @@ public void GetMostPopularCandidateDelta_should_return_the_favourite_with_most_v } } - [Fact] + [Test] public void GetMostPopularCandidateDelta_should_return_null_on_unknown_previous_delta_hash() { _cache.TryGetValue(Arg.Any(), out Arg.Any()).Returns(false); - var elector = new DeltaElector(_cache, _deltaProducersProvider, _logger); + DeltaElector elector = new(_cache, _deltaProducersProvider, _reputationManager, _logger); - var previousHash = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("previous")); + var previousHash = _hashProvider.ComputeUtf8MultiHash("previous").ToCid(); var popular = elector.GetMostPopularCandidateDelta(previousHash); @@ -261,7 +325,7 @@ public void GetMostPopularCandidateDelta_should_return_null_on_unknown_previous_ _logger.Received(1).Debug(Arg.Any(), Arg.Any()); } - private void AddVoterAsExpectedProducer(params PeerId[] producers) + private void AddVoterAsExpectedProducer(params Address[] producers) { _deltaProducersProvider .GetDeltaProducersFromPreviousDelta(Arg.Any()) diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaHashProviderTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaHashProviderTests.cs index e1e3934b2a..afaaad70a4 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaHashProviderTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaHashProviderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,19 +26,20 @@ using System.Reflection; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Hashing; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Abstractions.Sync; using Catalyst.Core.Modules.Consensus.Deltas; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Protocol.Deltas; using Catalyst.TestUtils; using FluentAssertions; using Google.Protobuf; -using LibP2P; +using Lib.P2P; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; +using Catalyst.Core.Lib.Service; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Deltas { @@ -46,41 +47,44 @@ public sealed class DeltaHashProviderTests : SelfAwareTestBase { //we just need an offset to not have TimeStamp = 0 when building deltas (cf DeltaHelper) private const int Offset = 100; - private readonly IDeltaCache _deltaCache; - private readonly ILogger _logger; - private readonly IHashProvider _hashProvider; + private IDeltaCache _deltaCache; + private ILogger _logger; + private IHashProvider _hashProvider; - public DeltaHashProviderTests(ITestOutputHelper output) : base(output) + [SetUp] + public void Init() { + this.Setup(TestContext.CurrentContext); + _deltaCache = Substitute.For(); _logger = new LoggerConfiguration() .MinimumLevel.Verbose() - .WriteTo.TestOutput(output) + .WriteTo.NUnitOutput() .CreateLogger() .ForContext(MethodBase.GetCurrentMethod().DeclaringType); - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); _deltaCache.GenesisHash.Returns( - CidHelper.CreateCid(_hashProvider.ComputeMultiHash(new Delta().ToByteArray()))); + _hashProvider.ComputeMultiHash(new Delta().ToByteArray()).ToCid()); } - [Fact] + [Test] public void Generate_Genesis_Hash() { - var emptyDelta = new Delta(); - var hash = CidHelper.CreateCid(_hashProvider.ComputeMultiHash(emptyDelta.ToByteArray())); + Delta emptyDelta = new(); + var hash = _hashProvider.ComputeMultiHash(emptyDelta.ToByteArray()).ToCid(); - Output.WriteLine(hash); + TestContext.WriteLine(hash); } - [Fact] + [Test] public void TryUpdateLatestHash_Should_Update_If_Hashes_Are_Valid() { const int deltaCount = 2; BuildDeltasAndSetCacheExpectations(deltaCount); - var hashProvider = new DeltaHashProvider(_deltaCache, _logger, 3); + DeltaHashProvider hashProvider = new(_deltaCache, Substitute.For(), _logger, 3); var updated = hashProvider.TryUpdateLatestHash(GetHash(0), GetHash(1)); updated.Should().BeTrue(); @@ -88,14 +92,14 @@ public void TryUpdateLatestHash_Should_Update_If_Hashes_Are_Valid() .Should().Be(GetHash(1)); } - [Fact] + [Test] public void TryUpdateLatestHash_Should_Push_New_Hash_On_Stream_When_Updating_Latest() { const int deltaCount = 2; BuildDeltasAndSetCacheExpectations(deltaCount); var observer = Substitute.For>(); - var hashProvider = new DeltaHashProvider(_deltaCache, _logger, 3); + DeltaHashProvider hashProvider = new(_deltaCache, Substitute.For(), _logger, 3); using (hashProvider.DeltaHashUpdates.Subscribe(observer)) { @@ -105,13 +109,13 @@ public void TryUpdateLatestHash_Should_Push_New_Hash_On_Stream_When_Updating_Lat } } - [Fact] + [Test] public void TryUpdateLatestHash_Should_Put_New_Hash_At_The_Top_Of_The_List() { const int deltaCount = 3; BuildDeltasAndSetCacheExpectations(deltaCount); - var hashProvider = new DeltaHashProvider(_deltaCache, _logger, 4); + DeltaHashProvider hashProvider = new(_deltaCache, Substitute.For(), _logger, 4); var updated = hashProvider.TryUpdateLatestHash(GetHash(0), GetHash(1)); updated.Should().BeTrue(); updated = hashProvider.TryUpdateLatestHash(GetHash(1), GetHash(2)); @@ -121,14 +125,14 @@ public void TryUpdateLatestHash_Should_Put_New_Hash_At_The_Top_Of_The_List() .Should().Be(GetHash(2)); } - [Fact] + [Test] public void DeltaHashProviderConstructor_Should_Apply_Capacity() { const int deltaCount = 9; BuildDeltasAndSetCacheExpectations(deltaCount); const int cacheCapacity = 3; - var deltaHashProvider = new DeltaHashProvider(_deltaCache, _logger, cacheCapacity); + DeltaHashProvider deltaHashProvider = new(_deltaCache, Substitute.For(), _logger, cacheCapacity); Enumerable.Range(1, deltaCount - 1).ToList().ForEach(i => { @@ -154,7 +158,7 @@ public void DeltaHashProviderConstructor_Should_Apply_Capacity() }); } - private DateTime GetDateTimeForIndex(int i) { return DateTime.FromOADate(Offset + i).ToUniversalTime(); } + private DateTime GetDateTimeForIndex(int i) { return DateTime.UnixEpoch.AddSeconds(i); } private Cid GetHash(int i) { diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaHubTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaHubTests.cs index 5965d7e4d9..ecc387eb31 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaHubTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaHubTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,173 +27,170 @@ using System.Threading.Tasks; using Catalyst.Abstractions.Dfs; using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Options; using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Consensus.Deltas; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Protocol.Wire; -using Catalyst.Protocol.Deltas; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; using FluentAssertions; using Google.Protobuf; +using MultiFormats.Registry; using NSubstitute; using Polly; using Polly.Retry; using Serilog; -using Xunit; -using LibP2P; -using TheDotNetLeague.MultiFormats.MultiHash; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Core.Modules.Kvm; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Deltas { public sealed class DeltaHubTests { - private readonly IHashProvider _hashProvider; - private readonly IBroadcastManager _broadcastManager; - private readonly PeerId _peerId; - private readonly DeltaHub _hub; - private readonly IDfs _dfs; + private IHashProvider _hashProvider; + private MultiAddress _address; + private DeltaHub _hub; + private IDfsService _dfsService; + private IPeerClient _peerClient; - internal sealed class DeltaHubWithFastRetryPolicy : DeltaHub + private sealed class DeltaHubWithFastRetryPolicy : DeltaHub { - public DeltaHubWithFastRetryPolicy(IBroadcastManager broadcastManager, + public DeltaHubWithFastRetryPolicy( + IPeerClient peerClient, IPeerSettings peerSettings, - IDfs dfs, - ILogger logger) : base(broadcastManager, peerSettings, dfs, logger) { } + IDfsService dfsService, + IHashProvider hashProvider, + ILogger logger) : base(peerClient, peerSettings, dfsService, hashProvider, logger) { } - protected override AsyncRetryPolicy DfsRetryPolicy => - Polly.Policy.Handle() + protected override AsyncRetryPolicy DfsRetryPolicy => + Policy.Handle() .WaitAndRetryAsync(4, retryAttempt => TimeSpan.FromMilliseconds(Math.Pow(2, retryAttempt))); } - public DeltaHubTests() + [SetUp] + public void Init() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - _broadcastManager = Substitute.For(); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + _peerClient = Substitute.For(); var logger = Substitute.For(); - _peerId = PeerIdHelper.GetPeerId("me"); - _dfs = Substitute.For(); - _hub = new DeltaHubWithFastRetryPolicy(_broadcastManager, _peerId.ToSubstitutedPeerSettings(), _dfs, logger); + _address = MultiAddressHelper.GetAddress("me"); + _dfsService = Substitute.For(); + _hub = new DeltaHubWithFastRetryPolicy(_peerClient, _address.ToSubstitutedPeerSettings(), _dfsService, _hashProvider, logger); } - [Fact] + [Test] public async Task BroadcastCandidate_should_not_broadcast_candidates_from_other_nodes() { var notMyCandidate = DeltaHelper.GetCandidateDelta(_hashProvider, - producerId: PeerIdHelper.GetPeerId("not me")); + producerId: MultiAddressHelper.GetAddress("not me").GetPublicKeyBytes().ToKvmAddress()); _hub.BroadcastCandidate(notMyCandidate); - await _broadcastManager.DidNotReceiveWithAnyArgs().BroadcastAsync(default).ConfigureAwait(false); + await _peerClient.DidNotReceiveWithAnyArgs().BroadcastAsync(default).ConfigureAwait(false); } - [Fact] + [Test] public void BroadcastCandidate_should_allow_broadcasting_candidate_from_this_node() { var myCandidate = DeltaHelper.GetCandidateDelta(_hashProvider, - producerId: _peerId); + producerId: _address.GetPublicKeyBytes().ToKvmAddress()); _hub.BroadcastCandidate(myCandidate); - _broadcastManager.Received(1)?.BroadcastAsync(Arg.Is( - m => IsExpectedCandidateMessage(m, myCandidate, _peerId))); + _peerClient.Received(1)?.BroadcastAsync(Arg.Is( + m => IsExpectedCandidateMessage(m, myCandidate, _address))); } - - [Fact] + + [Test] public void BroadcastFavouriteCandidateDelta_Should_Broadcast() { var favourite = new FavouriteDeltaBroadcast { Candidate = DeltaHelper.GetCandidateDelta(_hashProvider), - VoterId = _peerId + Voter = _address.GetPublicKeyBytes().ToKvmAddressByteString() }; _hub.BroadcastFavouriteCandidateDelta(favourite); - _broadcastManager.Received(1)?.BroadcastAsync(Arg.Is( - c => IsExpectedCandidateMessage(c, favourite, _peerId))); + _peerClient.Received(1)?.BroadcastAsync(Arg.Is( + c => IsExpectedCandidateMessage(c, favourite, _address))); } - [Fact] + [Test] public async Task PublishDeltaToIpfsAsync_should_return_ipfs_address() { var delta = DeltaHelper.GetDelta(_hashProvider); - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("lskdjaslkjfweoho")); - var cancellationToken = new CancellationToken(); + var cid = _hashProvider.ComputeUtf8MultiHash("i'm a string").ToCid(); + var fakeBlock = Substitute.For(); + fakeBlock.Id.Returns(cid); + CancellationToken cancellationToken = new(); - _dfs.AddAsync(Arg.Any(), Arg.Any(), cancellationToken).Returns(cid); + _dfsService.UnixFsApi.AddAsync(Arg.Any(), Arg.Any(), Arg.Any(), cancel: cancellationToken).Returns(fakeBlock); var deltaCid = await _hub.PublishDeltaToDfsAndBroadcastAddressAsync(delta, cancellationToken); deltaCid.Should().NotBeNull(); deltaCid.Should().Be(cid); } - [Fact] + [Test] public async Task PublishDeltaToIpfsAsync_should_retry_then_return_ipfs_address() { var delta = DeltaHelper.GetDelta(_hashProvider); - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("success")); + var cid = _hashProvider.ComputeUtf8MultiHash("success").ToCid(); - var dfsResults = new SubstituteResults(() => throw new Exception("this one failed")) + var fakeBlock = Substitute.For(); + fakeBlock.Id.Returns(cid); + var dfsResults = new SubstituteResults(() => throw new Exception("this one failed")) .Then(() => throw new Exception("this one failed too")) - .Then(cid); + .Then(fakeBlock); - _dfs.AddAsync(Arg.Any(), Arg.Any()) + _dfsService.UnixFsApi.AddAsync(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(ci => dfsResults.Next()); var deltaCid = await _hub.PublishDeltaToDfsAndBroadcastAddressAsync(delta); deltaCid.Should().NotBeNull(); deltaCid.Should().Be(cid); - await _dfs.ReceivedWithAnyArgs(3).AddAsync(Arg.Any(), Arg.Any()); + await _dfsService.UnixFsApi.ReceivedWithAnyArgs(3).AddAsync(Arg.Any(), Arg.Any(), Arg.Any()); } - [Fact] + [Test] public async Task PublishDeltaToIpfsAsync_should_retry_until_cancelled() { var delta = DeltaHelper.GetDelta(_hashProvider); - var dfsHash = "success"; - var cancellationSource = new CancellationTokenSource(); - var cancellationToken = cancellationSource.Token; + var cid = _hashProvider.ComputeUtf8MultiHash("success").ToCid(); + + var fakeBlock = Substitute.For(); + fakeBlock.Id.Returns(cid); - var dfsResults = new SubstituteResults(() => throw new Exception("this one failed")) + CancellationTokenSource cancellationSource = new(); + var cancellationToken = cancellationSource.Token; + + var dfsResults = new SubstituteResults(() => throw new Exception("this one failed")) .Then(() => throw new Exception("this one failed again")) .Then(() => { cancellationSource.Cancel(); throw new Exception("this one failed too"); }) - .Then(dfsHash); - - _dfs.AddAsync(Arg.Any(), Arg.Any(), cancellationToken) + .Then(fakeBlock); + + _dfsService.UnixFsApi.AddAsync(Arg.Any(), Arg.Any(), Arg.Any(), cancel: cancellationToken) .Returns(ci => dfsResults.Next()); - + new Action(() => _hub.PublishDeltaToDfsAndBroadcastAddressAsync(delta, cancellationToken).GetAwaiter().GetResult()) .Should().NotThrow(); - - await _dfs.ReceivedWithAnyArgs(3).AddAsync(Arg.Any(), Arg.Any()); + + await _dfsService.UnixFsApi.ReceivedWithAnyArgs(3).AddAsync(Arg.Any(), Arg.Any(), Arg.Any(), cancel: cancellationToken); } - public class BadDeltas : TheoryData - { - public BadDeltas() - { - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - var noPreviousHash = new Delta {PreviousDeltaDfsHash = (new byte[0]).ToByteString()}; - var noMerkleRoot = DeltaHelper.GetDelta(hashProvider, merkleRoot: new byte[0]); - - AddRow(noMerkleRoot, typeof(InvalidDataException)); - AddRow(noPreviousHash, typeof(InvalidDataException)); - AddRow(null as Delta, typeof(ArgumentNullException)); - } - } - - private bool IsExpectedCandidateMessage(ProtocolMessage protocolMessage, - T expected, - PeerId senderId) where T : IMessage + private static bool IsExpectedCandidateMessage(ProtocolMessage protocolMessage, + T expected, + MultiAddress sender) where T : IMessage { - var hasExpectedSender = protocolMessage.PeerId.Equals(senderId); + var hasExpectedSender = protocolMessage.Address == sender.ToString(); var candidate = protocolMessage.FromProtocolMessage(); var hasExpectedCandidate = candidate.Equals(expected); return hasExpectedSender && hasExpectedCandidate; diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaTransactionRetrieverTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaTransactionRetrieverTests.cs index e936be618d..6ad87f3024 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaTransactionRetrieverTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaTransactionRetrieverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,43 +26,44 @@ using System.Linq; using Catalyst.Abstractions.Mempool; using Catalyst.Core.Lib.DAO; -using Catalyst.Core.Lib.Extensions.Protocol.Wire; +using Catalyst.Core.Lib.DAO.Transaction; +using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Modules.Consensus.Deltas; -using Catalyst.Protocol.Wire; +using Catalyst.Protocol.Transaction; using Catalyst.TestUtils; using FluentAssertions; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Deltas { public sealed class DeltaTransactionRetrieverTests { - private readonly IList _transactions; + private readonly IList _transactions; private readonly DeltaTransactionRetriever _transactionRetriever; public DeltaTransactionRetrieverTests() { - var mapperProvider = new TestMapperProvider(); + TestMapperProvider mapperProvider = new(); - var random = new Random(); + Random random = new(); - var mempool = Substitute.For>(); + var mempool = Substitute.For>(); _transactions = Enumerable.Range(0, 20).Select(i => TransactionHelper.GetPublicTransaction( transactionFees: (ulong) random.Next(), timestamp: random.Next(), signature: i.ToString()) - ).ToList(); + ).Select(x => x.PublicEntry).ToList(); - mempool.Repository.GetAll().Returns(_transactions - .Select(x => x.ToDao(mapperProvider))); + mempool.Service.GetAll() + .Returns(_transactions.Select(x => x.ToDao(mapperProvider))); _transactionRetriever = new DeltaTransactionRetriever(mempool, mapperProvider, - TransactionComparerByFeeTimestampAndHash.Default); + TransactionComparerByPriceAndHash.Default); } - [Fact] + [Test] public void GetMempoolTransactionsByPriority_should_at_most_return_MaxCount() { var maxCountBelowTotal = _transactions.Count - 1; @@ -78,7 +79,7 @@ public void GetMempoolTransactionsByPriority_should_at_most_return_MaxCount() retrieved.Count.Should().Be(_transactions.Count); } - [Fact] + [Test] public void GetMempoolTransactionsByPriority_should_not_accept_zero_or_negative_maxCount() { new Action(() => _transactionRetriever.GetMempoolTransactionsByPriority(-1)) @@ -88,7 +89,7 @@ public void GetMempoolTransactionsByPriority_should_not_accept_zero_or_negative_ .Should().Throw(); } - [Fact] + [Test] public void GetMempoolTransactionsByPriority_should_return_transactions_in_decreasing_priority_order() { var maxCount = _transactions.Count / 2; @@ -105,7 +106,7 @@ public void GetMempoolTransactionsByPriority_should_return_transactions_in_decre .Take(excludedTransactionCount).ToList(); unexpectedTransactions - .ForEach(t => retrievedTransactions.Any(r => t.Signature == r.Signature).Should() + .ForEach(t => retrievedTransactions.Any(r => t.Signature.Equals(r.Signature)).Should() .BeFalse("No unexpected transactions should have been retrieved")); for (var i = 0; i < maxCount; i++) @@ -117,9 +118,9 @@ public void GetMempoolTransactionsByPriority_should_return_transactions_in_decre } // just a sanity check to make sure that the order is not opposite of what was intended in - // TransactionComparerByFeeTimestampAndHash - retrievedTransactions[i - 1].SummedEntryFees().Should() - .BeGreaterOrEqualTo(retrievedTransactions[i].SummedEntryFees()); + // TransactionComparerByPriceTimestampAndHash + retrievedTransactions[i - 1].GasPrice.ToUInt256().Should() + .BeGreaterOrEqualTo(retrievedTransactions[i].GasPrice.ToUInt256()); } } } diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaVoterTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaVoterTests.cs index 85d53c3bce..757c994fd5 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaVoterTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/DeltaVoterTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,105 +32,129 @@ using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Consensus.Deltas; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using FluentAssertions; -using LibP2P; +using Lib.P2P; using Microsoft.Extensions.Caching.Memory; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Core.Modules.Kvm; +using Nethermind.Core; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Deltas { public sealed class DeltaVoterTests : IDisposable { - public static readonly List DodgyCandidates; - - private readonly IHashProvider _hashProvider; - private readonly IMemoryCache _cache; - private readonly IDeltaProducersProvider _producersProvider; + private IHashProvider _hashProvider; + private IMemoryCache _cache; + private IDeltaProducersProvider _producersProvider; private DeltaVoter _voter; - private readonly Cid _previousDeltaHash; - private readonly IList _producerIds; - private readonly PeerId _localIdentifier; - private readonly ILogger _logger; - private readonly IPeerSettings _peerSettings; - - static DeltaVoterTests() - { - DodgyCandidates = new List - { - new object[] {null}, - new object[] {new CandidateDeltaBroadcast()}, - new object[] - { - new CandidateDeltaBroadcast - { - Hash = ByteUtil.GenerateRandomByteArray(32).ToByteString(), - PreviousDeltaDfsHash = ByteUtil.GenerateRandomByteArray(32).ToByteString() - } - }, - new object[] - { - new CandidateDeltaBroadcast - { - Hash = ByteUtil.GenerateRandomByteArray(32).ToByteString(), - ProducerId = PeerIdHelper.GetPeerId("unknown_producer") - } - }, - new object[] - { - new CandidateDeltaBroadcast - { - PreviousDeltaDfsHash = ByteUtil.GenerateRandomByteArray(32).ToByteString(), - ProducerId = PeerIdHelper.GetPeerId("unknown_producer") - } - } - }; - } - - public DeltaVoterTests() + private Cid _previousDeltaHash; + private IList
_producerIds; + private MultiAddress _localIdentifier; + private ILogger _logger; + private IPeerSettings _peerSettings; + + [SetUp] + public void Init() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); _cache = Substitute.For(); _previousDeltaHash = - CidHelper.CreateCid(_hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32))); + _hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32)).ToCid(); _producerIds = "1234" - .Select((c, i) => PeerIdHelper.GetPeerId(c.ToString())) + .Select((c, i) => MultiAddressHelper.GetAddress(c.ToString()).GetPublicKeyBytes().ToKvmAddress()) .Shuffle(); _producersProvider = Substitute.For(); _producersProvider.GetDeltaProducersFromPreviousDelta(Arg.Any()) .Returns(_producerIds); - _localIdentifier = PeerIdHelper.GetPeerId("myself, a producer"); + _localIdentifier = MultiAddressHelper.GetAddress("myself, a producer"); _peerSettings = _localIdentifier.ToSubstitutedPeerSettings(); _logger = Substitute.For(); } - [Theory] - [MemberData(nameof(DodgyCandidates))] - public void When_candidate_is_dodgy_should_log_and_return_without_hitting_the_cache(CandidateDeltaBroadcast dodgyCandidate) + [Test] + public void When_candidate_is_null_should_log_and_return_without_hitting_the_cache() + { + _voter = new DeltaVoter(_cache, _producersProvider, _peerSettings, _logger); + + _voter.OnNext(null); + + _cache.DidNotReceiveWithAnyArgs().TryGetValue(Arg.Any(), out Arg.Any()); + _cache.DidNotReceiveWithAnyArgs().CreateEntry(Arg.Any()); + } + + [Test] + public void When_candidate_is_empty_should_log_and_return_without_hitting_the_cache() { _voter = new DeltaVoter(_cache, _producersProvider, _peerSettings, _logger); - _voter.OnNext(dodgyCandidate); + _voter.OnNext(new CandidateDeltaBroadcast()); + + _cache.DidNotReceiveWithAnyArgs().TryGetValue(Arg.Any(), out Arg.Any()); + _cache.DidNotReceiveWithAnyArgs().CreateEntry(Arg.Any()); + } + + [Test] + public void When_candidate_producer_id_is_empty_should_log_and_return_without_hitting_the_cache() + { + _voter = new DeltaVoter(_cache, _producersProvider, _peerSettings, _logger); + + _voter.OnNext(new CandidateDeltaBroadcast + { + Hash = ByteUtil.GenerateRandomByteArray(32).ToByteString(), + PreviousDeltaDfsHash = ByteUtil.GenerateRandomByteArray(32).ToByteString() + }); + + _cache.DidNotReceiveWithAnyArgs().TryGetValue(Arg.Any(), out Arg.Any()); + _cache.DidNotReceiveWithAnyArgs().CreateEntry(Arg.Any()); + } + + [Test] + public void When_candidate_previous_delta_hash_is_empty_should_log_and_return_without_hitting_the_cache() + { + _voter = new DeltaVoter(_cache, _producersProvider, _peerSettings, _logger); + + _voter.OnNext(new CandidateDeltaBroadcast + { + Hash = ByteUtil.GenerateRandomByteArray(32).ToByteString(), + Producer = MultiAddressHelper.GetAddress("unknown_producer").GetPublicKeyBytes().ToKvmAddressByteString() + }); + + _cache.DidNotReceiveWithAnyArgs().TryGetValue(Arg.Any(), out Arg.Any()); + _cache.DidNotReceiveWithAnyArgs().CreateEntry(Arg.Any()); + } + + [Test] + public void When_candidate_hash_is_empty_should_log_and_return_without_hitting_the_cache() + { + _voter = new DeltaVoter(_cache, _producersProvider, _peerSettings, _logger); + + _voter.OnNext(new CandidateDeltaBroadcast + { + PreviousDeltaDfsHash = ByteUtil.GenerateRandomByteArray(32).ToByteString(), + Producer = MultiAddressHelper.GetAddress("unknown_producer").GetPublicKeyBytes().ToKvmAddressByteString() + }); _cache.DidNotReceiveWithAnyArgs().TryGetValue(Arg.Any(), out Arg.Any()); _cache.DidNotReceiveWithAnyArgs().CreateEntry(Arg.Any()); } - [Fact] + [Test] public void When_candidate_is_produced_by_unexpected_producer_should_log_and_return_without_hitting_the_cache() { var candidateFromUnknownProducer = DeltaHelper.GetCandidateDelta(_hashProvider, - producerId: PeerIdHelper.GetPeerId("unknown_producer")); + producerId: MultiAddressHelper.GetAddress("unknown_producer").GetPublicKeyBytes().ToKvmAddress()); _voter = new DeltaVoter(_cache, _producersProvider, _peerSettings, _logger); _voter.OnNext(candidateFromUnknownProducer); @@ -142,7 +166,7 @@ public void When_candidate_is_produced_by_unexpected_producer_should_log_and_ret _cache.DidNotReceiveWithAnyArgs().CreateEntry(Arg.Any()); } - [Fact] + [Test] public void When_candidate_not_in_cache_should_build_ScoredCandidate_with_ranking_and_store_it() { _voter = new DeltaVoter(_cache, _producersProvider, _peerSettings, _logger); @@ -151,7 +175,7 @@ public void When_candidate_not_in_cache_should_build_ScoredCandidate_with_rankin _previousDeltaHash, producerId: _producerIds.First()); - var candidateHash = CidHelper.Cast(candidate.Hash.ToByteArray()); + var candidateHash = candidate.Hash.ToByteArray().ToCid(); var addedEntry = Substitute.For(); _cache.CreateEntry(Arg.Is(s => s.EndsWith(candidateHash))) @@ -171,7 +195,7 @@ public void When_candidate_not_in_cache_should_build_ScoredCandidate_with_rankin scoredCandidateDelta.Score.Should().Be(100 * _producerIds.Count + 1); } - [Fact] + [Test] public void When_candidate_in_cache_should_retrieve_ScoredCandidate() { _voter = new DeltaVoter(_cache, _producersProvider, _peerSettings, _logger); @@ -183,7 +207,7 @@ public void When_candidate_in_cache_should_retrieve_ScoredCandidate() previousDeltaHash: _previousDeltaHash, score: initialScore); - var candidateHash = CidHelper.Cast(cacheCandidate.Candidate.Hash.ToByteArray()); + var candidateHash = cacheCandidate.Candidate.Hash.ToByteArray().ToCid(); _cache.TryGetValue(Arg.Any(), out Arg.Any()).Returns(ci => { @@ -200,10 +224,10 @@ public void When_candidate_in_cache_should_retrieve_ScoredCandidate() cacheCandidate.Score.Should().Be(initialScore + 1); } - [Fact] + [Test] public void When_second_candidate_is_more_popular_it_should_score_higher() { - using (var realCache = new MemoryCache(new MemoryCacheOptions())) + using (MemoryCache realCache = new(new MemoryCacheOptions())) { _voter = new DeltaVoter(realCache, _producersProvider, _peerSettings, _logger); @@ -246,10 +270,10 @@ private List AddCandidatesToCacheAndVote(int firstVotesCo } } - [Fact] + [Test] public void When_candidates_not_in_cache_should_create_or_update_a_previous_hash_entry() { - using (var realCache = new MemoryCache(new MemoryCacheOptions())) + using (MemoryCache realCache = new(new MemoryCacheOptions())) { _voter = new DeltaVoter(realCache, _producersProvider, _peerSettings, _logger); @@ -271,7 +295,7 @@ public void When_candidates_not_in_cache_should_create_or_update_a_previous_hash realCache.TryGetValue(candidate1CacheKey, out ScoredCandidateDelta retrievedCandidate1).Should().BeTrue(); - retrievedCandidate1.Candidate.ProducerId.Should().Be(_producerIds.First()); + retrievedCandidate1.Candidate.Producer.Should().BeEquivalentTo(_producerIds.First().Bytes.ToByteString()); realCache.TryGetValue(previousDeltaCacheKey, out ConcurrentBag retrievedCandidateList).Should().BeTrue(); @@ -281,7 +305,7 @@ public void When_candidates_not_in_cache_should_create_or_update_a_previous_hash realCache.TryGetValue(candidate2CacheKey, out ScoredCandidateDelta retrievedCandidate2).Should().BeTrue(); - retrievedCandidate2.Candidate.ProducerId.Should().Be(_producerIds.Last()); + retrievedCandidate2.Candidate.Producer.Should().BeEquivalentTo(_producerIds.Last().Bytes.ToByteString()); realCache.TryGetValue(previousDeltaCacheKey, out ConcurrentBag retrievedUpdatedCandidateList).Should().BeTrue(); @@ -289,10 +313,10 @@ public void When_candidates_not_in_cache_should_create_or_update_a_previous_hash } } - [Fact] + [Test] public void GetFavouriteDelta_should_retrieve_favourite_delta() { - using (var realCache = new MemoryCache(new MemoryCacheOptions())) + using (MemoryCache realCache = new(new MemoryCacheOptions())) { _voter = new DeltaVoter(realCache, _producersProvider, _peerSettings, _logger); @@ -301,7 +325,7 @@ public void GetFavouriteDelta_should_retrieve_favourite_delta() scoredCandidates[1].Score.Should().BeGreaterThan(scoredCandidates[0].Score); var previousDeltaHash = - CidHelper.Cast(scoredCandidates[0].Candidate.PreviousDeltaDfsHash.ToByteArray()); + scoredCandidates[0].Candidate.PreviousDeltaDfsHash.ToByteArray().ToCid(); var found = _voter.TryGetFavouriteDelta(previousDeltaHash, out var favouriteCandidate); @@ -311,21 +335,21 @@ public void GetFavouriteDelta_should_retrieve_favourite_delta() .SequenceEqual(previousDeltaHash.ToArray()).Should().BeTrue(); favouriteCandidate.Candidate.Hash.ToByteArray() .SequenceEqual(scoredCandidates[1].Candidate.Hash.ToByteArray()).Should().BeTrue(); - favouriteCandidate.Candidate.ProducerId.Should().Be(scoredCandidates[1].Candidate.ProducerId); + favouriteCandidate.Candidate.Producer.Should().BeEquivalentTo(scoredCandidates[1].Candidate.Producer); } } - [Fact] + [Test] public void GetFavouriteDelta_should_return_null_on_unknown_previous_delta_hash() { - using (var realCache = new MemoryCache(new MemoryCacheOptions())) + using (MemoryCache realCache = new(new MemoryCacheOptions())) { _voter = new DeltaVoter(realCache, _producersProvider, _peerSettings, _logger); AddCandidatesToCacheAndVote(10, 500, realCache); var found = _voter.TryGetFavouriteDelta( - CidHelper.CreateCid(_hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32))), + _hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32)).ToCid(), out var favouriteCandidate); found.Should().BeFalse(); @@ -333,10 +357,10 @@ public void GetFavouriteDelta_should_return_null_on_unknown_previous_delta_hash( } } - [Fact] + [Test] public void GetFavouriteDelta_should_return_lowest_hash_when_candidate_scores_are_equal() { - using (var realCache = new MemoryCache(new MemoryCacheOptions())) + using (MemoryCache realCache = new(new MemoryCacheOptions())) { _voter = new DeltaVoter(realCache, _producersProvider, _peerSettings, _logger); @@ -347,7 +371,7 @@ public void GetFavouriteDelta_should_return_lowest_hash_when_candidate_scores_ar scoredCandidates.Select(c => c.Candidate.PreviousDeltaDfsHash).Distinct().Count().Should().Be(1); var found = _voter.TryGetFavouriteDelta( - CidHelper.Cast(scoredCandidates.First().Candidate.PreviousDeltaDfsHash.ToByteArray()), + scoredCandidates.First().Candidate.PreviousDeltaDfsHash.ToByteArray().ToCid(), out var favouriteCandidate); found.Should().BeTrue(); diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/FavouriteByHashAndVoterComparerTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/FavouriteByHashAndVoterComparerTests.cs index 16e35ee5ac..a833d92c77 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/FavouriteByHashAndVoterComparerTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/Deltas/FavouriteByHashAndVoterComparerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,99 +27,108 @@ using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; +using Google.Protobuf; +using MultiFormats; +using NUnit.Framework; using CandidateDeltaBroadcast = Catalyst.Protocol.Wire.CandidateDeltaBroadcast; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.Deltas { public class FavouriteByHashAndVoterComparerTests { - public class FavouritesComparisonData : TheoryData - { - public FavouritesComparisonData() - { - Add(null, null, true); - Add(new FavouriteDeltaBroadcast(), new FavouriteDeltaBroadcast(), true); - Add(null, new FavouriteDeltaBroadcast(), false); - Add(new FavouriteDeltaBroadcast(), null, false); + private static MultiAddress voter1 = MultiAddressHelper.GetAddress("voter1"); + private static MultiAddress voter2 = MultiAddressHelper.GetAddress("voter2"); - var voter1 = PeerIdHelper.GetPeerId("voter1"); - var voter2 = PeerIdHelper.GetPeerId("voter2"); + private static MultiAddress producer1 = MultiAddressHelper.GetAddress("producer1"); + private static MultiAddress producer2 = MultiAddressHelper.GetAddress("producer2"); - var producer1 = PeerIdHelper.GetPeerId("producer1"); - var producer2 = PeerIdHelper.GetPeerId("producer2"); + private static ByteString hash1 = ByteUtil.GenerateRandomByteArray(32).ToByteString(); + private static ByteString hash2 = ByteUtil.GenerateRandomByteArray(32).ToByteString(); - var hash1 = ByteUtil.GenerateRandomByteArray(32).ToByteString(); - var hash2 = ByteUtil.GenerateRandomByteArray(32).ToByteString(); + private static ByteString previousHash1 = ByteUtil.GenerateRandomByteArray(32).ToByteString(); + private static ByteString previousHash2 = ByteUtil.GenerateRandomByteArray(32).ToByteString(); - var previousHash1 = ByteUtil.GenerateRandomByteArray(32).ToByteString(); - var previousHash2 = ByteUtil.GenerateRandomByteArray(32).ToByteString(); + public class FavouritesTestData + { + public FavouriteDeltaBroadcast X { private set; get; } + public FavouriteDeltaBroadcast Y { private set; get; } + public bool ComparisonResult { private set; get; } + public FavouritesTestData(FavouriteDeltaBroadcast x, FavouriteDeltaBroadcast y, bool comparisonResult) + { + X = x; + Y = y; + ComparisonResult = comparisonResult; + } + } - Add(new FavouriteDeltaBroadcast + private static FavouritesTestData[] FavouritesComparisonData = new FavouritesTestData[]{ + new FavouritesTestData(null, null, true), + new FavouritesTestData(new FavouriteDeltaBroadcast(), new FavouriteDeltaBroadcast(), true ), + new FavouritesTestData(null, new FavouriteDeltaBroadcast(), false ), + new FavouritesTestData(new FavouriteDeltaBroadcast(), null, false ), + new FavouritesTestData(new FavouriteDeltaBroadcast + { + Candidate = new CandidateDeltaBroadcast { - Candidate = new CandidateDeltaBroadcast - { - Hash = hash1, ProducerId = producer1, PreviousDeltaDfsHash = previousHash1 - }, - VoterId = voter1 + Hash = hash1, Producer = producer1.GetKvmAddressByteString(), PreviousDeltaDfsHash = previousHash1 }, - new FavouriteDeltaBroadcast + Voter = voter1.GetKvmAddressByteString() + }, + new FavouriteDeltaBroadcast + { + Candidate = new CandidateDeltaBroadcast { - Candidate = new CandidateDeltaBroadcast - { - Hash = hash2, ProducerId = producer1, PreviousDeltaDfsHash = previousHash1 - }, - VoterId = voter1 - }, false); + Hash = hash2, Producer = producer1.GetKvmAddressByteString(), PreviousDeltaDfsHash = previousHash1 + }, + Voter = voter1.GetKvmAddressByteString() + }, false ), - Add(new FavouriteDeltaBroadcast + new FavouritesTestData(new FavouriteDeltaBroadcast + { + Candidate = new CandidateDeltaBroadcast { - Candidate = new CandidateDeltaBroadcast - { - Hash = hash1, ProducerId = producer1, PreviousDeltaDfsHash = previousHash1 - }, - VoterId = voter1 + Hash = hash1, Producer = producer1.GetKvmAddressByteString(), PreviousDeltaDfsHash = previousHash1 }, - new FavouriteDeltaBroadcast + Voter = voter1.GetKvmAddressByteString() + }, + new FavouriteDeltaBroadcast + { + Candidate = new CandidateDeltaBroadcast { - Candidate = new CandidateDeltaBroadcast - { - Hash = hash1, ProducerId = producer1, PreviousDeltaDfsHash = previousHash1 - }, - VoterId = voter2 - }, false); + Hash = hash1, Producer = producer1.GetKvmAddressByteString(), PreviousDeltaDfsHash = previousHash1 + }, + Voter = voter2.GetKvmAddressByteString() + }, false ), - Add(new FavouriteDeltaBroadcast + new FavouritesTestData(new FavouriteDeltaBroadcast + { + Candidate = new CandidateDeltaBroadcast { - Candidate = new CandidateDeltaBroadcast - { - Hash = hash1, ProducerId = producer1, PreviousDeltaDfsHash = previousHash1 - }, - VoterId = voter1 - }, - new FavouriteDeltaBroadcast + Hash = hash1, Producer = producer1.GetKvmAddressByteString(), PreviousDeltaDfsHash = previousHash1 + }, + Voter = voter1.GetKvmAddressByteString() + }, + new FavouriteDeltaBroadcast + { + Candidate = new CandidateDeltaBroadcast { - Candidate = new CandidateDeltaBroadcast - { - Hash = hash1, ProducerId = producer2, PreviousDeltaDfsHash = previousHash2 - }, - VoterId = voter1 - }, true); - } - } - - [Theory] - [ClassData(typeof(FavouritesComparisonData))] - public void FavouriteByHashComparer_should_differentiate_by_candidate_hash_and_voter_only(FavouriteDeltaBroadcast x, FavouriteDeltaBroadcast y, bool comparisonResult) + Hash = hash1, Producer = producer2.GetKvmAddressByteString(), PreviousDeltaDfsHash = previousHash2 + }, + Voter = voter1.GetKvmAddressByteString() + }, true) + }; + + [TestCaseSource(nameof(FavouritesComparisonData))] + public void FavouriteByHashComparer_should_differentiate_by_candidate_hash_and_voter_only(FavouritesTestData favouritesTestData) { - var xHashCode = FavouriteByHashAndVoterComparer.Default.GetHashCode(x); - var yHashCode = FavouriteByHashAndVoterComparer.Default.GetHashCode(y); + var xHashCode = FavouriteByHashAndVoterComparer.Default.GetHashCode(favouritesTestData.X); + var yHashCode = FavouriteByHashAndVoterComparer.Default.GetHashCode(favouritesTestData.Y); if (xHashCode != 0 && yHashCode != 0) { - xHashCode.Equals(yHashCode).Should().Be(comparisonResult); + xHashCode.Equals(yHashCode).Should().Be(favouritesTestData.ComparisonResult); } - FavouriteByHashAndVoterComparer.Default.Equals(x, y).Should().Be(comparisonResult); + FavouriteByHashAndVoterComparer.Default.Equals(favouritesTestData.X, favouritesTestData.Y).Should().Be(favouritesTestData.ComparisonResult); } } } diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/CandidateDeltaObserverTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/CandidateDeltaObserverTests.cs index 135ff521db..cf387c7d8e 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/CandidateDeltaObserverTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/CandidateDeltaObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,94 +24,95 @@ using System.Linq; using System.Text; using Catalyst.Abstractions.Consensus.Deltas; -using Catalyst.Abstractions.IO.Messaging.Dto; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Consensus.IO.Observers; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; -using DotNetty.Transport.Channels; -using LibP2P; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; +using Google.Protobuf; +using System.Collections.Generic; +using Lib.P2P; +using MultiFormats; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.IO.Observers { public sealed class CandidateDeltaObserverTests { - private readonly IDeltaVoter _deltaVoter; - private readonly IChannelHandlerContext _fakeChannelContext; - private readonly Cid _newHash; - private readonly Cid _prevHash; - private readonly PeerId _producerId; - private readonly CandidateDeltaObserver _candidateDeltaObserver; - - public CandidateDeltaObserverTests() + private IDeltaVoter _deltaVoter; + private Cid _newHash; + private Cid _prevHash; + private MultiAddress _producer; + private CandidateDeltaObserver _candidateDeltaObserver; + + [SetUp] + public void Init() { - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + HashProvider hashProvider = new(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); _deltaVoter = Substitute.For(); - _fakeChannelContext = Substitute.For(); var logger = Substitute.For(); - _newHash = CidHelper.CreateCid(hashProvider.ComputeUtf8MultiHash("newHash")); - _prevHash = CidHelper.CreateCid(hashProvider.ComputeUtf8MultiHash("prevHash")); - _producerId = PeerIdHelper.GetPeerId("candidate delta producer"); - _candidateDeltaObserver = new CandidateDeltaObserver(_deltaVoter, hashProvider, logger); + _newHash = hashProvider.ComputeUtf8MultiHash("newHash").ToCid(); + _prevHash = hashProvider.ComputeUtf8MultiHash("prevHash").ToCid(); + _producer = MultiAddressHelper.GetAddress("candidate delta producer"); + + var peerRepository = Substitute.For(); + peerRepository.GetPoaPeersByPublicKey(Arg.Any()).Returns(new List { new Lib.P2P.Models.Peer() }); + + _candidateDeltaObserver = new CandidateDeltaObserver(_deltaVoter, peerRepository, hashProvider, logger); } - [Fact] + [Test] public void HandleBroadcast_Should_Cast_Hashes_To_Multihash_And_Send_To_Voter() { - var receivedMessage = PrepareReceivedMessage(_newHash.ToArray(), _prevHash.ToArray(), _producerId); + var receivedMessage = PrepareReceivedMessage(_newHash.ToArray(), _prevHash.ToArray(), _producer); _candidateDeltaObserver.HandleBroadcast(receivedMessage); _deltaVoter.Received(1).OnNext(Arg.Is(c => c.Hash.SequenceEqual(_newHash.ToArray().ToByteString()) && c.PreviousDeltaDfsHash.Equals(_prevHash.ToArray().ToByteString()) - && c.ProducerId.Equals(_producerId))); + && c.Producer == _producer.GetKvmAddressByteString())); } - [Fact] + [Test] public void HandleBroadcast_Should_Not_Try_Forwarding_Invalid_Hash() { var invalidNewHash = Encoding.UTF8.GetBytes("invalid hash"); - var receivedMessage = PrepareReceivedMessage(invalidNewHash, _prevHash.ToArray(), _producerId); + var receivedMessage = PrepareReceivedMessage(invalidNewHash, _prevHash.ToArray(), _producer); _candidateDeltaObserver.HandleBroadcast(receivedMessage); _deltaVoter.DidNotReceiveWithAnyArgs().OnNext(default); } - [Fact] + [Test] public void HandleBroadcast_Should_Not_Try_Forwarding_Invalid_PreviousHash() { var invalidPreviousHash = Encoding.UTF8.GetBytes("invalid previous hash"); - var receivedMessage = PrepareReceivedMessage(_newHash.ToArray(), invalidPreviousHash, _producerId); + var receivedMessage = PrepareReceivedMessage(_newHash.ToArray(), invalidPreviousHash, _producer); _candidateDeltaObserver.HandleBroadcast(receivedMessage); _deltaVoter.DidNotReceiveWithAnyArgs().OnNext(default); } - private IObserverDto PrepareReceivedMessage(byte[] newHash, + private ProtocolMessage PrepareReceivedMessage(byte[] newHash, byte[] prevHash, - PeerId producerId) + MultiAddress producer) { var message = new CandidateDeltaBroadcast { Hash = newHash.ToByteString(), PreviousDeltaDfsHash = prevHash.ToByteString(), - ProducerId = producerId + Producer = producer.GetKvmAddressByteString() }; - var receivedMessage = new ObserverDto(_fakeChannelContext, - message.ToProtocolMessage(PeerIdHelper.GetPeerId())); - return receivedMessage; + return message.ToProtocolMessage(MultiAddressHelper.GetAddress()); } } } diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/DeltaDfsHashObserverTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/DeltaDfsHashObserverTests.cs index 0cf91b958d..52acdab352 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/DeltaDfsHashObserverTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/DeltaDfsHashObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,69 +21,126 @@ #endregion +using System.Collections.Generic; using System.Text; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Hashing; -using Catalyst.Abstractions.IO.Messaging.Dto; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Core.Abstractions.Sync; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Lib.P2P.Models; using Catalyst.Core.Modules.Consensus.IO.Observers; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Core.Modules.Kvm; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.IO.Observers { public sealed class DeltaDfsHashObserverTests { - private readonly IHashProvider _hashProvider; - private readonly IDeltaHashProvider _deltaHashProvider; - private readonly IChannelHandlerContext _fakeChannelContext; - private readonly ILogger _logger; + private IHashProvider _hashProvider; + private IDeltaHashProvider _deltaHashProvider; + private SyncState _syncState; + private ILogger _logger; + private IPeerRepository _peerRepository; - public DeltaDfsHashObserverTests() + [SetUp] + public void Init() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); _deltaHashProvider = Substitute.For(); - _fakeChannelContext = Substitute.For(); + _syncState = new SyncState { IsSynchronized = true }; _logger = Substitute.For(); + _peerRepository = Substitute.For(); } - [Fact] + [Test] public void HandleBroadcast_Should_Cast_Hashes_To_Multihash_And_Try_Update() { - var newHash = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("newHash")); - var prevHash = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("prevHash")); + var newHash = _hashProvider.ComputeUtf8MultiHash("newHash").ToCid(); + var prevHash = _hashProvider.ComputeUtf8MultiHash("prevHash").ToCid(); var receivedMessage = PrepareReceivedMessage(newHash.ToArray(), prevHash.ToArray()); - var deltaDfsHashObserver = new DeltaDfsHashObserver(_deltaHashProvider, _logger); + MultiAddress multiAddress = new(receivedMessage.Address); + + var candidateDeltaBroadcast = new CandidateDeltaBroadcast + { + PreviousDeltaDfsHash = prevHash.ToArray().ToByteString(), + Producer = multiAddress.GetPublicKeyBytes().ToKvmAddressByteString() + }; + + var deltaElector = Substitute.For(); + deltaElector.GetMostPopularCandidateDelta(prevHash).Returns(candidateDeltaBroadcast); + + _peerRepository.GetPoaPeersByPublicKey(multiAddress.GetPublicKey()).Returns(new List() { new Peer() }); + DeltaDfsHashObserver deltaDfsHashObserver = new(_deltaHashProvider, deltaElector, _syncState, _peerRepository, _logger); deltaDfsHashObserver.HandleBroadcast(receivedMessage); _deltaHashProvider.Received(1).TryUpdateLatestHash(prevHash, newHash); } - [Fact] + [Test] public void HandleBroadcast_Should_Not_Try_Update_Invalid_Hash() { var invalidNewHash = Encoding.UTF8.GetBytes("invalid hash"); - var prevHash = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("prevHash")); + var prevHash = _hashProvider.ComputeUtf8MultiHash("prevHash").ToCid(); var receivedMessage = PrepareReceivedMessage(invalidNewHash, prevHash.ToArray()); - var deltaDfsHashObserver = new DeltaDfsHashObserver(_deltaHashProvider, _logger); + MultiAddress multiAddress = new(receivedMessage.Address); + + var candidateDeltaBroadcast = new CandidateDeltaBroadcast + { + PreviousDeltaDfsHash = prevHash.ToArray().ToByteString(), + Producer = multiAddress.GetPublicKeyBytes().ToKvmAddressByteString() + }; + + var deltaElector = Substitute.For(); + deltaElector.GetMostPopularCandidateDelta(prevHash).Returns(candidateDeltaBroadcast); + + DeltaDfsHashObserver deltaDfsHashObserver = new(_deltaHashProvider, deltaElector, _syncState, _peerRepository, _logger); deltaDfsHashObserver.HandleBroadcast(receivedMessage); _deltaHashProvider.DidNotReceiveWithAnyArgs().TryUpdateLatestHash(default, default); } - private IObserverDto PrepareReceivedMessage(byte[] newHash, byte[] prevHash) + [Test] + public void HandleBroadcast_Should_Not_Try_Update_Invalid_Peer() + { + var newHash = _hashProvider.ComputeUtf8MultiHash("newHash").ToCid(); + var prevHash = _hashProvider.ComputeUtf8MultiHash("prevHash").ToCid(); + var receivedMessage = PrepareReceivedMessage(newHash.ToArray(), prevHash.ToArray()); + + MultiAddress multiAddress = new(receivedMessage.Address); + + var candidateDeltaBroadcast = new CandidateDeltaBroadcast + { + PreviousDeltaDfsHash = prevHash.ToArray().ToByteString(), + Producer = multiAddress.GetPublicKeyBytes().ToKvmAddressByteString() + }; + + var deltaElector = Substitute.For(); + deltaElector.GetMostPopularCandidateDelta(prevHash).Returns(candidateDeltaBroadcast); + + _peerRepository.GetPoaPeersByPublicKey(multiAddress.GetPublicKey()).Returns(new List()); + DeltaDfsHashObserver deltaDfsHashObserver = new(_deltaHashProvider, deltaElector, _syncState, _peerRepository, _logger); + + deltaDfsHashObserver.HandleBroadcast(receivedMessage); + + _deltaHashProvider.Received(0).TryUpdateLatestHash(prevHash, newHash); + _logger.Received(1).Error(Arg.Any()); + } + + private ProtocolMessage PrepareReceivedMessage(byte[] newHash, byte[] prevHash) { var message = new DeltaDfsHashBroadcast { @@ -91,9 +148,7 @@ private IObserverDto PrepareReceivedMessage(byte[] newHash, byt PreviousDeltaDfsHash = prevHash.ToByteString() }; - var receivedMessage = new ObserverDto(_fakeChannelContext, - message.ToProtocolMessage(PeerIdHelper.GetPeerId())); - return receivedMessage; + return message.ToProtocolMessage(MultiAddressHelper.GetAddress()); } } } diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/FavouriteDeltaObserverTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/FavouriteDeltaObserverTests.cs index 350e2979e0..898f394fbb 100644 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/FavouriteDeltaObserverTests.cs +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/IO/Observers/FavouriteDeltaObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,49 +24,54 @@ using System.Linq; using System.Text; using Catalyst.Abstractions.Consensus.Deltas; -using Catalyst.Abstractions.IO.Messaging.Dto; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Consensus.IO.Observers; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; +using MultiFormats; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiBase; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; +using Google.Protobuf; +using System.Collections.Generic; +using Catalyst.Core.Lib.P2P.Models; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests.IO.Observers { public sealed class FavouriteDeltaObserverTests { - private readonly IDeltaElector _deltaElector; - private readonly IChannelHandlerContext _fakeChannelContext; - private readonly PeerId _voterId; - private readonly PeerId _producerId; - private readonly FavouriteDeltaObserver _favouriteDeltaObserver; - private readonly byte[] _newHash; - private readonly byte[] _prevHash; - - public FavouriteDeltaObserverTests() + private IDeltaElector _deltaElector; + private IChannelHandlerContext _fakeChannelContext; + private MultiAddress _voterId; + private MultiAddress _producerId; + private FavouriteDeltaObserver _favouriteDeltaObserver; + private byte[] _newHash; + private byte[] _prevHash; + + [SetUp] + public void Init() { - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); _deltaElector = Substitute.For(); _fakeChannelContext = Substitute.For(); var logger = Substitute.For(); - _voterId = PeerIdHelper.GetPeerId("favourite delta voter"); - _producerId = PeerIdHelper.GetPeerId("candidate delta producer"); + _voterId = MultiAddressHelper.GetAddress("favourite delta voter"); + _producerId = MultiAddressHelper.GetAddress("candidate delta producer"); - _favouriteDeltaObserver = new FavouriteDeltaObserver(_deltaElector, hashProvider, logger); - _newHash = MultiBase.Decode(CidHelper.CreateCid(hashProvider.ComputeUtf8MultiHash("newHash"))); - _prevHash = MultiBase.Decode(CidHelper.CreateCid(hashProvider.ComputeUtf8MultiHash("prevHash"))); + var peerRepository = Substitute.For(); + peerRepository.GetPoaPeersByPublicKey(Arg.Any()).Returns(new List { new Peer() }); + + _favouriteDeltaObserver = new FavouriteDeltaObserver(_deltaElector, peerRepository, hashProvider, logger); + _newHash = MultiBase.Decode(hashProvider.ComputeUtf8MultiHash("newHash").ToCid()); + _prevHash = MultiBase.Decode(hashProvider.ComputeUtf8MultiHash("prevHash").ToCid()); } - [Fact] + [Test] public void HandleBroadcast_Should_Cast_Hashes_To_Multihash_And_Send_To_Voter() { var receivedMessage = PrepareReceivedMessage(_newHash, _prevHash, _producerId, _voterId); @@ -76,10 +81,10 @@ public void HandleBroadcast_Should_Cast_Hashes_To_Multihash_And_Send_To_Voter() _deltaElector.Received(1).OnNext(Arg.Is(c => c.Candidate.Hash.SequenceEqual(_newHash.ToArray().ToByteString()) && c.Candidate.PreviousDeltaDfsHash.Equals(_prevHash.ToArray().ToByteString()) - && c.Candidate.ProducerId.Equals(_producerId))); + && c.Candidate.Producer == _producerId.GetKvmAddressByteString())); } - [Fact] + [Test] public void HandleBroadcast_Should_Not_Try_Forwarding_Invalid_Hash() { var invalidNewHash = Encoding.UTF8.GetBytes("invalid hash"); @@ -91,7 +96,7 @@ public void HandleBroadcast_Should_Not_Try_Forwarding_Invalid_Hash() _deltaElector.DidNotReceiveWithAnyArgs().OnNext(default); } - [Fact] + [Test] public void HandleBroadcast_Should_Not_Try_Forwarding_Invalid_PreviousHash() { var invalidPrevHash = Encoding.UTF8.GetBytes("invalid previous hash"); @@ -103,27 +108,25 @@ public void HandleBroadcast_Should_Not_Try_Forwarding_Invalid_PreviousHash() _deltaElector.DidNotReceiveWithAnyArgs().OnNext(default); } - private IObserverDto PrepareReceivedMessage(byte[] newHash, + private ProtocolMessage PrepareReceivedMessage(byte[] newHash, byte[] prevHash, - PeerId producerId, - PeerId voterId) + MultiAddress producerId, + MultiAddress voterId) { var candidate = new CandidateDeltaBroadcast { Hash = newHash.ToByteString(), PreviousDeltaDfsHash = prevHash.ToByteString(), - ProducerId = producerId + Producer = producerId.GetKvmAddressByteString() }; var favouriteDeltaBroadcast = new FavouriteDeltaBroadcast { Candidate = candidate, - VoterId = voterId + Voter = voterId.GetKvmAddressByteString() }; - var receivedMessage = new ObserverDto(_fakeChannelContext, - favouriteDeltaBroadcast.ToProtocolMessage(PeerIdHelper.GetPeerId())); - return receivedMessage; + return favouriteDeltaBroadcast.ToProtocolMessage(MultiAddressHelper.GetAddress()); } } } diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/TransactionComparerByFeeTimestampAndHashTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/TransactionComparerByFeeTimestampAndHashTests.cs deleted file mode 100644 index f51da54f0c..0000000000 --- a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/TransactionComparerByFeeTimestampAndHashTests.cs +++ /dev/null @@ -1,146 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using Catalyst.Core.Lib.Extensions.Protocol.Wire; -using Catalyst.Core.Lib.Util; -using Catalyst.TestUtils; -using FluentAssertions; -using Google.Protobuf; -using Xunit; -using Xunit.Abstractions; - -namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests -{ - public class TransactionComparerByFeeTimestampAndHashTests - { - private readonly ITestOutputHelper _output; - private readonly Random _random; - - public TransactionComparerByFeeTimestampAndHashTests(ITestOutputHelper output) - { - _output = output; - _random = new Random(); - } - - [Fact] - public void Comparer_should_Order_By_Fees_First() - { - var transactions = Enumerable.Range(0, 100) - .Select(i => TransactionHelper.GetPublicTransaction( - transactionFees: (ulong) _random.Next(int.MaxValue), - timestamp: _random.Next(int.MaxValue), - signature: _random.Next(int.MaxValue).ToString()) - ).ToList(); - - var ordered = transactions - .OrderByDescending(t => t, TransactionComparerByFeeTimestampAndHash.Default) - .ToArray(); - - ordered.Select(o => o.SummedEntryFees()).Should().BeInDescendingOrder(t => t); - ordered.Select(t => t.Timestamp.ToDateTime()).Should().NotBeAscendingInOrder(); - ordered.Should().NotBeInDescendingOrder(t => t.Signature.ToByteArray(), ByteUtil.ByteListMinSizeComparer.Default); - } - - [Fact] - public void Comparer_should_Order_By_Fees_First_Then_By_TimeStamp() - { - var transactions = Enumerable.Range(0, 100) - .Select(i => TransactionHelper.GetPublicTransaction( - transactionFees: (ulong) i % 3, - timestamp: _random.Next(int.MaxValue), - signature: _random.Next(int.MaxValue).ToString()) - ).ToList(); - - var ordered = transactions - .OrderByDescending(t => t, TransactionComparerByFeeTimestampAndHash.Default) - .ToArray(); - - ordered.Select(o => o.SummedEntryFees()).Should().BeInDescendingOrder(t => t); - ordered.Select(t => t.Timestamp.ToDateTime()).Should().NotBeDescendingInOrder(); - - Enumerable.Range(0, 3).ToList().ForEach(i => - ordered.Where(t => t.SummedEntryFees() == (ulong) i) - .Select(t => t.Timestamp.ToDateTime()).Should().BeInAscendingOrder()); - - ordered.Should().NotBeInAscendingOrder(t => t.Signature.ToByteArray(), ByteUtil.ByteListMinSizeComparer.Default); - } - - [Fact] - public void Comparer_should_Order_By_Fees_First_Then_By_TimeStamp_Then_By_Signature() - { - var transactions = Enumerable.Range(0, 100) - .Select(i => TransactionHelper.GetPublicTransaction( - transactionFees: (ulong) i % 2, - timestamp: i % 3, - signature: _random.Next(int.MaxValue).ToString()) - ).ToList(); - - var ordered = transactions - .OrderByDescending(t => t, TransactionComparerByFeeTimestampAndHash.Default) - .ToArray(); - - ordered.Select(s => - s.SummedEntryFees() + "|" + s.Timestamp + "|" + - s.Signature.RawBytes.ToBase64()) - .ToList().ForEach(x => _output.WriteLine(x)); - - ordered.Select(o => o.SummedEntryFees()).Should().BeInDescendingOrder(t => t); - - Enumerable.Range(0, 2).ToList().ForEach(i => - { - ordered - .Select(t => t.SummedEntryFees() == (ulong) i ? t.Timestamp.Seconds : int.MaxValue) - .ToArray() - .Where(z => z != int.MaxValue) - .Should().BeInAscendingOrder(); - - Enumerable.Range(0, 3).ToList().ForEach(j => - ordered.Where(t => t.SummedEntryFees() == (ulong) i - && t.Timestamp.ToDateTime() == DateTime.FromOADate(j)).ToArray() - .Select(t => t.Signature.ToByteArray()) - .Should().BeInAscendingOrder(t => t, ByteUtil.ByteListMinSizeComparer.Default)); - }); - - ordered.Select(s => - s.SummedEntryFees() + "|" + s.Timestamp + "|" + - s.Signature.RawBytes.ToBase64()) - .ToList().ForEach(x => _output.WriteLine(x)); - - ordered.Should() - .NotBeInAscendingOrder(t => t.Signature.ToByteArray(), ByteUtil.ByteListMinSizeComparer.Default); - } - } - - public class ByteStringComparer : IComparer - { - public int Compare(ByteString x, ByteString y) - { - return ByteUtil.ByteListMinSizeComparer.Default.Compare(x.ToByteArray(), y.ToByteArray()); - } - - public static ByteStringComparer Default { get; } = new ByteStringComparer(); - } -} diff --git a/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/TransactionComparerByPriceAndHashTests.cs b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/TransactionComparerByPriceAndHashTests.cs new file mode 100644 index 0000000000..bb6a603d40 --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus.Tests/UnitTests/TransactionComparerByPriceAndHashTests.cs @@ -0,0 +1,126 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.Util; +using Catalyst.TestUtils; +using FluentAssertions; +using Google.Protobuf; +using NUnit.Framework; + + +namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests +{ + public class TransactionComparerByPriceAndHashTests + { + private TestContext _output; + private Random _random; + + [SetUp] + public void Init() + { + _output = TestContext.CurrentContext; + _random = new Random(); + } + + [Test] + public void Comparer_should_Order_By_GasPrice_First() + { + var transactions = Enumerable.Range(0, 100) + .Select(i => TransactionHelper.GetPublicTransaction( + transactionFees: (ulong) _random.Next(int.MaxValue), + gasPrice: (ulong) _random.Next(int.MaxValue), + timestamp: _random.Next(int.MaxValue), + signature: _random.Next(int.MaxValue).ToString()).PublicEntry + ).ToList(); + + var ordered = transactions + .OrderByDescending(t => t, TransactionComparerByPriceAndHash.Default) + .ToArray(); + + ordered.Select(o => o.GasPrice.ToUInt256()).Should().BeInDescendingOrder(t => t); + ordered.Select(t => t.Signature.ToByteArray()).Should().NotBeInDescendingOrder(t => t, ByteUtil.ByteListMinSizeComparer.Default); + } + + [Test] + public void Comparer_should_Order_By_GasPrice() + { + var transactions = Enumerable.Range(0, 100) + .Select(i => TransactionHelper.GetPublicTransaction( + gasPrice: (ulong) i % 3, + timestamp: _random.Next(int.MaxValue), + signature: _random.Next(int.MaxValue).ToString()).PublicEntry + ).ToList(); + + var ordered = transactions + .OrderByDescending(t => t, TransactionComparerByPriceAndHash.Default) + .ToArray(); + + ordered.Select(o => o.GasPrice.ToUInt256()).Should().BeInDescendingOrder(t => t); + + ordered.Select(t => t.Signature.ToByteArray()).Should().NotBeInDescendingOrder(t => t, ByteUtil.ByteListMinSizeComparer.Default); + } + + [Test] + public void Comparer_should_Order_By_GasPrice_Then_By_Signature() + { + var transactions = Enumerable.Range(0, 100) + .Select(i => TransactionHelper.GetPublicTransaction( + gasPrice: (ulong) i % 2, + timestamp: i % 3, + signature: _random.Next(int.MaxValue).ToString()).PublicEntry + ).ToList(); + + var ordered = transactions + .OrderByDescending(t => t, TransactionComparerByPriceAndHash.Default) + .ToArray(); + + ordered.Select(s => + s.GasPrice + "|" + + s.Signature.RawBytes.ToBase64()) + .ToList().ForEach(x => TestContext.WriteLine(x)); + + ordered.Select(o => o.GasPrice.ToUInt256()).Should().BeInDescendingOrder(t => t); + + ordered.Select(s => + s.GasPrice.ToUInt256() + "|" + + s.Signature.RawBytes.ToBase64()) + .ToList().ForEach(x => TestContext.WriteLine(x)); + + ordered.Select(t => t.Signature.ToByteArray()).Should().NotBeInDescendingOrder(t => t, ByteUtil.ByteListMinSizeComparer.Default); + } + } + + public class ByteStringComparer : IComparer + { + public int Compare(ByteString x, ByteString y) + { + return ByteUtil.ByteListMinSizeComparer.Default.Compare(x.ToByteArray(), y.ToByteArray()); + } + + public static ByteStringComparer Default { get; } = new ByteStringComparer(); + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Catalyst.Core.Modules.Consensus.csproj b/src/Catalyst.Core.Modules.Consensus/Catalyst.Core.Modules.Consensus.csproj index d7023a336b..fc6d53c4fa 100644 --- a/src/Catalyst.Core.Modules.Consensus/Catalyst.Core.Modules.Consensus.csproj +++ b/src/Catalyst.Core.Modules.Consensus/Catalyst.Core.Modules.Consensus.csproj @@ -1,20 +1,29 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Consensus - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Consensus.snk true + + 1701;1702;CS8002 + + + + + + + - - + + diff --git a/src/Catalyst.Core.Modules.Consensus/Consensus.cs b/src/Catalyst.Core.Modules.Consensus/Consensus.cs index 7c21093e44..eba8bd558b 100644 --- a/src/Catalyst.Core.Modules.Consensus/Consensus.cs +++ b/src/Catalyst.Core.Modules.Consensus/Consensus.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,9 +26,11 @@ using Catalyst.Abstractions.Consensus; using Catalyst.Abstractions.Consensus.Cycle; using Catalyst.Abstractions.Consensus.Deltas; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Consensus.Cycle; using Serilog; +using Catalyst.Core.Lib.Service; +using System.Threading.Tasks; namespace Catalyst.Core.Modules.Consensus { @@ -66,14 +68,19 @@ public Consensus(IDeltaBuilder deltaBuilder, _deltaHub = deltaHub; _deltaCache = deltaCache; _logger = logger; - logger.Information("Consensus service initialised."); + logger.Information("Consensus repository initialised."); } public void StartProducing() { + _cycleEventsProvider.StartAsync().ConfigureAwait(false).GetAwaiter().GetResult(); + _constructionProducingSubscription = _cycleEventsProvider.PhaseChanges - .Where(p => p.Name == PhaseName.Construction && p.Status == PhaseStatus.Producing) - .Select(p => _deltaBuilder.BuildCandidateDelta(p.PreviousDeltaDfsHash)) + .Where(p => p.Name.Equals(PhaseName.Construction) && p.Status.Equals(PhaseStatus.Producing)) + .Select(p => { + return _deltaBuilder.BuildCandidateDelta(p.PreviousDeltaDfsHash); + }) + .Where(c => c != null) .Subscribe(c => { _deltaVoter.OnNext(c); @@ -81,7 +88,7 @@ public void StartProducing() }); _campaigningProductionSubscription = _cycleEventsProvider.PhaseChanges - .Where(p => p.Name == PhaseName.Campaigning && p.Status == PhaseStatus.Producing) + .Where(p => p.Name.Equals(PhaseName.Campaigning) && p.Status.Equals(PhaseStatus.Producing)) .Select(p => { _deltaVoter.TryGetFavouriteDelta(p.PreviousDeltaDfsHash, out var favourite); @@ -95,8 +102,11 @@ public void StartProducing() }); _votingProductionSubscription = _cycleEventsProvider.PhaseChanges - .Where(p => p.Name == PhaseName.Voting && p.Status == PhaseStatus.Producing) - .Select(p => _deltaElector.GetMostPopularCandidateDelta(p.PreviousDeltaDfsHash)) + .Where(p => p.Name.Equals(PhaseName.Voting) && p.Status.Equals(PhaseStatus.Producing)) + .Select(p => + { + return _deltaElector.GetMostPopularCandidateDelta(p.PreviousDeltaDfsHash); + }) .Where(c => c != null) .Select(c => { @@ -104,19 +114,33 @@ public void StartProducing() return delta; }) .Where(d => d != null) - .Subscribe(d => + .Subscribe(async d => { - _logger.Information("New Delta following {deltaHash} published", + // here were some importnt changes for Web3 so need to have a look if I can delete the comments + // <<<<<<< HEAD + // var newCid = _deltaHub.PublishDeltaToDfsAndBroadcastAddressAsync(d) + // .ConfigureAwait(false).GetAwaiter().GetResult(); + // _deltaCache.AddLocalDelta(newCid, d); + // + // var previousHash = d.PreviousDeltaDfsHash.ToByteArray().ToCid(); + // + // _logger.Information("New Delta following {deltaHash} published with new cid {newCid}", + // d.PreviousDeltaDfsHash, newCid); + // + // _deltaHashProvider.TryUpdateLatestHash(previousHash, newCid); + // ======= + + _logger.Information("New Delta following {deltaHash} published", d.PreviousDeltaDfsHash); - var newCid = _deltaHub.PublishDeltaToDfsAndBroadcastAddressAsync(d) + var newHashCid = _deltaHub.PublishDeltaToDfsAndBroadcastAddressAsync(d) .ConfigureAwait(false).GetAwaiter().GetResult(); - var previousHash = CidHelper.Cast(d.PreviousDeltaDfsHash.ToByteArray()); + var previousHashCid = d.PreviousDeltaDfsHash.ToByteArray().ToCid(); - _deltaHashProvider.TryUpdateLatestHash(previousHash, newCid.Hash); + _deltaHashProvider.TryUpdateLatestHash(previousHashCid, newHashCid); }); } - + public void Dispose() { _constructionProducingSubscription?.Dispose(); diff --git a/src/Catalyst.Core.Modules.Consensus/ConsensusModule.cs b/src/Catalyst.Core.Modules.Consensus/ConsensusModule.cs index 09cae89b48..448571ec62 100644 --- a/src/Catalyst.Core.Modules.Consensus/ConsensusModule.cs +++ b/src/Catalyst.Core.Modules.Consensus/ConsensusModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,37 +28,41 @@ using Catalyst.Abstractions.IO.Observers; using Catalyst.Core.Modules.Consensus.Cycle; using Catalyst.Core.Modules.Consensus.Deltas; +using Catalyst.Core.Modules.Consensus.Deltas.Building; using Catalyst.Core.Modules.Consensus.IO.Observers; +using Catalyst.Core.Modules.Kvm; namespace Catalyst.Core.Modules.Consensus { - public class ConsensusModule : Module + public class ConsensusModule : Autofac.Module { protected override void Load(ContainerBuilder builder) { // we will put this in its own module eventually builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As().WithParameter("timeToLiveInMs", 600000); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); builder.RegisterType().As(); - builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance().WithParameter("capacity", 10_000); - builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance() + .WithExecutionParameters(builder) + .WithStateDbParameters(builder); + builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As(); + builder.RegisterType().As(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); builder.RegisterType().As(); builder.RegisterType().As().SingleInstance(); builder.RegisterInstance(CycleConfiguration.Default).As(); - base.Load(builder); + builder.RegisterType().As().SingleInstance() + .WithExecutionParameters(builder); } } } diff --git a/src/Catalyst.Core.Modules.Consensus/Cycle/CycleConfiguration.cs b/src/Catalyst.Core.Modules.Consensus/Cycle/CycleConfiguration.cs index 986952449a..b9c56afd46 100644 --- a/src/Catalyst.Core.Modules.Consensus/Cycle/CycleConfiguration.cs +++ b/src/Catalyst.Core.Modules.Consensus/Cycle/CycleConfiguration.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,7 +30,7 @@ namespace Catalyst.Core.Modules.Consensus.Cycle /// public class CycleConfiguration : ICycleConfiguration { - protected CycleConfiguration(PhaseTimings construction, + protected CycleConfiguration(IPhaseTimings construction, IPhaseTimings campaigning, IPhaseTimings voting, IPhaseTimings synchronisation) @@ -68,30 +68,30 @@ protected CycleConfiguration(PhaseTimings construction, private static readonly TimeSpan ConstructionProduction = TimeSpan.FromSeconds(2); private static readonly TimeSpan ConstructionCollection = TimeSpan.FromSeconds(2); - private static readonly PhaseTimings DefaultConstructionTimings = + private static readonly IPhaseTimings DefaultConstructionTimings = new PhaseTimings(ConstructionOffset, ConstructionProduction, ConstructionCollection); private static readonly TimeSpan CampaigningOffset = ConstructionOffset + ConstructionProduction + ConstructionCollection; private static readonly TimeSpan CampaigningProduction = TimeSpan.FromSeconds(3); private static readonly TimeSpan CampaigningCollection = TimeSpan.FromSeconds(3); - private static readonly PhaseTimings DefaultCampaigningTimings = + private static readonly IPhaseTimings DefaultCampaigningTimings = new PhaseTimings(CampaigningOffset, CampaigningProduction, CampaigningCollection); private static readonly TimeSpan VotingOffset = CampaigningOffset + CampaigningProduction + CampaigningCollection; private static readonly TimeSpan VotingProduction = TimeSpan.FromSeconds(3); private static readonly TimeSpan VotingCollection = TimeSpan.FromSeconds(2); - private static readonly PhaseTimings DefaultVotingTimings = + private static readonly IPhaseTimings DefaultVotingTimings = new PhaseTimings(VotingOffset, VotingProduction, VotingCollection); private static readonly TimeSpan SynchronisationOffset = VotingOffset + VotingProduction + VotingCollection; private static readonly TimeSpan SynchronisationProduction = TimeSpan.FromSeconds(1); private static readonly TimeSpan SynchronisationCollection = TimeSpan.FromSeconds(3); - private static readonly PhaseTimings DefaultSynchronisationTimings = + private static readonly IPhaseTimings DefaultSynchronisationTimings = new PhaseTimings(SynchronisationOffset, SynchronisationProduction, SynchronisationCollection); - public static readonly CycleConfiguration Default = new CycleConfiguration(DefaultConstructionTimings, DefaultCampaigningTimings, DefaultVotingTimings, DefaultSynchronisationTimings); + public static readonly ICycleConfiguration Default = new CycleConfiguration(DefaultConstructionTimings, DefaultCampaigningTimings, DefaultVotingTimings, DefaultSynchronisationTimings); } } diff --git a/src/Catalyst.Core.Modules.Consensus/Cycle/CycleEventsProvider.cs b/src/Catalyst.Core.Modules.Consensus/Cycle/CycleEventsProvider.cs index 04e42926c3..b67368e546 100644 --- a/src/Catalyst.Core.Modules.Consensus/Cycle/CycleEventsProvider.cs +++ b/src/Catalyst.Core.Modules.Consensus/Cycle/CycleEventsProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,9 +22,11 @@ #endregion using System; +using System.Linq; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Threading; +using System.Threading.Tasks; using Catalyst.Abstractions.Consensus; using Catalyst.Abstractions.Consensus.Cycle; using Catalyst.Abstractions.Consensus.Deltas; @@ -34,11 +36,14 @@ namespace Catalyst.Core.Modules.Consensus.Cycle { /// /// + [Obsolete] public class CycleEventsProvider : ICycleEventsProvider, IDisposable { private readonly CancellationTokenSource _cancellationTokenSource; protected readonly IScheduler Scheduler; private readonly IDateTimeProvider _dateTimeProvider; + private readonly IDeltaHashProvider _deltaHashProvider; + private readonly ILogger _logger; public CycleEventsProvider(ICycleConfiguration configuration, IDateTimeProvider timeProvider, @@ -47,10 +52,17 @@ public CycleEventsProvider(ICycleConfiguration configuration, ILogger logger) { _cancellationTokenSource = new CancellationTokenSource(); - + Configuration = configuration; Scheduler = schedulerProvider.Scheduler; + _logger = logger; + + _dateTimeProvider = timeProvider; + _deltaHashProvider = deltaHashProvider; + } + public Task StartAsync() + { var constructionStatusChanges = StatefulPhase.GetStatusChangeObservable( PhaseName.Construction, Configuration.Construction, Configuration.CycleDuration, Scheduler); @@ -63,7 +75,6 @@ public CycleEventsProvider(ICycleConfiguration configuration, var synchronisationStatusChanges = StatefulPhase.GetStatusChangeObservable( PhaseName.Synchronisation, Configuration.Synchronisation, Configuration.CycleDuration, Scheduler); - _dateTimeProvider = timeProvider; var synchronisationOffset = GetTimeSpanUntilNextCycleStart(); PhaseChanges = constructionStatusChanges @@ -71,18 +82,19 @@ public CycleEventsProvider(ICycleConfiguration configuration, .Merge(votingStatusChanges, Scheduler) .Merge(synchronisationStatusChanges, Scheduler) .Delay(synchronisationOffset, Scheduler) - .Select(s => new Phase(deltaHashProvider.GetLatestDeltaHash(_dateTimeProvider.UtcNow), s.Name, s.Status, _dateTimeProvider.UtcNow)) - .Do(p => logger.Debug("Current delta production phase {phase}", p), - exception => logger.Error(exception, "{PhaseChanges} stream failed and will stop producing cycle events.", nameof(PhaseChanges)), - () => logger.Debug("Stream {PhaseChanges} completed.", nameof(PhaseChanges))) + .Select(s => new Phase(_deltaHashProvider.GetLatestDeltaHash(_dateTimeProvider.UtcNow), s.Name, s.Status, _dateTimeProvider.UtcNow)) + .Do(p => _logger.Debug("Current delta production phase {phase}", p), + exception => _logger.Error(exception, "{PhaseChanges} stream failed and will stop producing cycle events.", nameof(PhaseChanges)), + () => _logger.Debug("Stream {PhaseChanges} completed.", nameof(PhaseChanges))) .TakeWhile(_ => !_cancellationTokenSource.IsCancellationRequested); + return Task.CompletedTask; } public TimeSpan GetTimeSpanUntilNextCycleStart() { var cycleDurationTicks = _dateTimeProvider.UtcNow.Ticks % Configuration.CycleDuration.Ticks; var ticksUntilNextCycleStart = cycleDurationTicks == 0 - ? 0 + ? 0 : Configuration.CycleDuration.Ticks - cycleDurationTicks; return TimeSpan.FromTicks(ticksUntilNextCycleStart); } @@ -91,7 +103,7 @@ public TimeSpan GetTimeSpanUntilNextCycleStart() public ICycleConfiguration Configuration { get; } /// - public IObservable PhaseChanges { get; } + public IObservable PhaseChanges { private set; get; } /// public void Close() { _cancellationTokenSource.Cancel(); } diff --git a/src/Catalyst.Core.Modules.Consensus/Cycle/CycleEventsProviderRaw.cs b/src/Catalyst.Core.Modules.Consensus/Cycle/CycleEventsProviderRaw.cs new file mode 100644 index 0000000000..6efddc872a --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus/Cycle/CycleEventsProviderRaw.cs @@ -0,0 +1,275 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Consensus; +using Catalyst.Abstractions.Consensus.Cycle; +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Enumerator; +using Catalyst.Core.Modules.Consensus.Cycle; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Threading; +using System.Threading.Tasks; +using Serilog; +using Catalyst.Protocol.Deltas; +using Catalyst.Abstractions.Validators; +using Catalyst.Abstractions.KeySigner; +using Catalyst.Abstractions.Types; +using Catalyst.Core.Modules.Kvm; +using Nethermind.Core; + +namespace Catalyst.Core.Modules.Consensus +{ + /// + public class CycleEventsProviderRaw : ICycleEventsProvider, IDisposable + { + private readonly CancellationTokenSource _cancellationTokenSource; + private readonly IDateTimeProvider _dateTimeProvider; + private readonly IDeltaHashProvider _deltaHashProvider; + private readonly IDeltaCache _deltaCache; + private readonly IValidatorSetStore _validatorSetStore; + private readonly IKeySigner _keySigner; + private readonly Address _kvmAddress; + + private readonly IList _orderedPhaseNamesByOffset; + private readonly IList _orderedPhaseStatuses; + private readonly IDictionary> _phaseOffsetMappings; + + private readonly ReplaySubject _phaseChangesMessageSubject; + public IObservable PhaseChanges { set; get; } + + public ICycleConfiguration Configuration { get; } + + private readonly ILogger _logger; + + public CycleEventsProviderRaw(ICycleConfiguration configuration, + IDateTimeProvider dateTimeProvider, + IDeltaHashProvider deltaHashProvider, + IDeltaCache deltaCache, + IValidatorSetStore validatorSetStore, + IKeySigner keySigner, + ILogger logger) + { + _cancellationTokenSource = new CancellationTokenSource(); + + Configuration = configuration; + _dateTimeProvider = dateTimeProvider; + _deltaHashProvider = deltaHashProvider; + _deltaCache = deltaCache; + _validatorSetStore = validatorSetStore; + _logger = logger; + + var privateKey = keySigner.GetPrivateKey(KeyRegistryTypes.DefaultKey); + var publicKey = privateKey.GetPublicKey(); + _kvmAddress = publicKey.ToKvmAddress(); + + _phaseChangesMessageSubject = new ReplaySubject(); + PhaseChanges = _phaseChangesMessageSubject.AsObservable(); + + _orderedPhaseNamesByOffset = CreateAndOrderPhaseNamesByOffset(Configuration); + _orderedPhaseStatuses = CreatePhaseStatuses(); + _phaseOffsetMappings = CreatePhaseOffsetMappings(); + + //Idle phase status is not needed, it is to keep timing in RXObserver event cycles. + _orderedPhaseStatuses.Remove(PhaseStatus.Idle); + _phaseOffsetMappings.Remove(PhaseStatus.Idle); + } + + /// + public TimeSpan GetTimeSpanUntilNextCycleStart() + { + var cycleDurationTicks = _dateTimeProvider.UtcNow.Ticks % Configuration.CycleDuration.Ticks; + var ticksUntilNextCycleStart = cycleDurationTicks == 0 + ? 0 + : Configuration.CycleDuration.Ticks - cycleDurationTicks; + return TimeSpan.FromTicks(ticksUntilNextCycleStart); + } + + /// + /// Use this method to find what time the next production cycle will start. + /// + /// A DateTime representing the time until next delta production cycle starts. + private DateTime GetDateUntilNextCycleStart() + { + return _dateTimeProvider.UtcNow.Add(GetTimeSpanUntilNextCycleStart()); + } + + /// + /// Use this method to create and order all the PhaseNames + /// + /// The cycle configuration settings + /// A list of all the PhaseNames + private static IList CreateAndOrderPhaseNamesByOffset(ICycleConfiguration configuration) + { + return configuration.TimingsByName.Keys.OrderBy(x => configuration.TimingsByName[x].Offset).ToList(); + } + + /// + /// Use this method to create all the PhaseStatuses + /// + /// A list of all the PhaseStatus + private static IList CreatePhaseStatuses() + { + return Enumeration.GetAll().Cast().ToList(); + } + + /// + /// Use this method to create all the PhaseStatus to PhaseTiming mappings. + /// + /// A dictionary of all the PhaseStatus to PhaseTiming mappings + private static IDictionary> CreatePhaseOffsetMappings() + { + return new Dictionary> + { + { PhaseStatus.Producing, x => x.Offset }, + { PhaseStatus.Collecting, x => x.Offset + x.ProductionTime }, + { PhaseStatus.Idle, x => x.Offset + x.TotalTime } + }; + } + + /// + public Task StartAsync() + { + var eventCycleThread = new Thread(new ThreadStart(EventCycleLoopThread)) + { + IsBackground = true, + Priority = ThreadPriority.Highest + }; + eventCycleThread.Start(); + return Task.CompletedTask; + } + + /// + /// EventCycle Thread to loop through each cycle continuously, we want time precision for each phase. + /// + private void EventCycleLoopThread() + { + while (!_cancellationTokenSource.IsCancellationRequested) + { + var currentDeltaCid = _deltaHashProvider.GetLatestDeltaHash(_dateTimeProvider.UtcNow); + _deltaCache.TryGetOrAddConfirmedDelta(currentDeltaCid, out Delta currentDelta); + var currentDeltaHeight = currentDelta.DeltaNumber; + + StartCycle(++currentDeltaHeight); + } + + _logger.Debug("Stream {PhaseChanges} completed.", nameof(PhaseChanges)); + _phaseChangesMessageSubject.OnCompleted(); + _phaseChangesMessageSubject.Dispose(); + } + + /// + /// Each cycle will produce 8 phase events excluding 'idle' phase status: 4 phases * 2 phase statuses = 8 phase events. + /// + /// The current cycle + private void StartCycle(long cycleNumber) + { + var phaseNumber = 0; + var statusNumber = 0; + + var startTime = GetDateUntilNextCycleStart(); + var nextCycleStartTime = startTime.Add(Configuration.CycleDuration); + + var validators = _validatorSetStore.Get(cycleNumber); + var isValidator = validators.GetValidators().Contains(_kvmAddress); + + _logger.Debug($"Event Provider Cycle {cycleNumber } Starting at: {startTime}"); + _logger.Debug($"Event Provider Cycle {cycleNumber + 1} Starting at: {nextCycleStartTime}"); + + while (phaseNumber < _orderedPhaseNamesByOffset.Count && !_cancellationTokenSource.IsCancellationRequested) + { + var currentPhaseName = _orderedPhaseNamesByOffset[phaseNumber]; + var currentPhaseTiming = Configuration.TimingsByName[currentPhaseName]; + var currentPhaseStatus = _orderedPhaseStatuses[statusNumber]; + + var phase = GetPhase(startTime, _phaseOffsetMappings[currentPhaseStatus](currentPhaseTiming), currentPhaseName, currentPhaseStatus); + if (phase != null) + { + _logger.Debug("Current delta production phase {phase}", phase); + if (isValidator) + { + _phaseChangesMessageSubject.OnNext(phase); + } + statusNumber++; + } + + if (statusNumber >= _orderedPhaseStatuses.Count) + { + statusNumber = 0; + phaseNumber++; + } + + Thread.Sleep(10); + } + } + + /// + /// Find's the current phase after the specified time in the cycle. + /// + /// The cycle start time. + /// The offset the phase will run in the cycle. + /// The name of the phase. + /// The phase status + /// The phase with the corresponding data. + private IPhase GetPhase(DateTime cycleStartTime, TimeSpan phaseOffset, IPhaseName phaseName, IPhaseStatus phaseStatus) + { + var phaseStartTime = cycleStartTime.Add(phaseOffset); + if (_dateTimeProvider.UtcNow.Ticks >= phaseStartTime.Ticks) + { + return new Phase(_deltaHashProvider.GetLatestDeltaHash(_dateTimeProvider.UtcNow), phaseName, phaseStatus, _dateTimeProvider.UtcNow); + } + + return null; + } + + /// + public void Close() + { + _cancellationTokenSource.Cancel(); + } + + protected virtual void Dispose(bool disposing) + { + if (!disposing) + { + return; + } + + if (!_cancellationTokenSource.IsCancellationRequested) + { + Close(); + } + + _cancellationTokenSource.Dispose(); + } + + /// + public void Dispose() + { + Dispose(true); + } + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Cycle/CycleSchedulerProvider.cs b/src/Catalyst.Core.Modules.Consensus/Cycle/CycleSchedulerProvider.cs index b395e593a8..d7a234c9d7 100644 --- a/src/Catalyst.Core.Modules.Consensus/Cycle/CycleSchedulerProvider.cs +++ b/src/Catalyst.Core.Modules.Consensus/Cycle/CycleSchedulerProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Consensus/Cycle/Phase.cs b/src/Catalyst.Core.Modules.Consensus/Cycle/Phase.cs index a7aad72fb7..8b3e75be84 100644 --- a/src/Catalyst.Core.Modules.Consensus/Cycle/Phase.cs +++ b/src/Catalyst.Core.Modules.Consensus/Cycle/Phase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,7 @@ using System; using Catalyst.Abstractions.Consensus.Cycle; -using LibP2P; +using Lib.P2P; namespace Catalyst.Core.Modules.Consensus.Cycle { diff --git a/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseName.cs b/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseName.cs index afa71dc19f..8daacb3198 100644 --- a/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseName.cs +++ b/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseName.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseStatus.cs b/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseStatus.cs index cd88ebf215..3053b461e3 100644 --- a/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseStatus.cs +++ b/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseStatus.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseTimings.cs b/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseTimings.cs index 1f02a3cf94..b1c1891967 100644 --- a/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseTimings.cs +++ b/src/Catalyst.Core.Modules.Consensus/Cycle/PhaseTimings.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Consensus/Cycle/StatefulPhase.cs b/src/Catalyst.Core.Modules.Consensus/Cycle/StatefulPhase.cs index c4000650e6..67dfd89ba6 100644 --- a/src/Catalyst.Core.Modules.Consensus/Cycle/StatefulPhase.cs +++ b/src/Catalyst.Core.Modules.Consensus/Cycle/StatefulPhase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Consensus/DateTimeProvider.cs b/src/Catalyst.Core.Modules.Consensus/DateTimeProvider.cs index 8cd3a7fec9..04fa3ddc6c 100644 --- a/src/Catalyst.Core.Modules.Consensus/DateTimeProvider.cs +++ b/src/Catalyst.Core.Modules.Consensus/DateTimeProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,7 @@ namespace Catalyst.Core.Modules.Consensus { /// - public class DateTimeProvider : IDateTimeProvider + public sealed class DateTimeProvider : IDateTimeProvider { /// public DateTime UtcNow => DateTime.UtcNow; diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/Building/AveragePriceComparer.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/AveragePriceComparer.cs new file mode 100644 index 0000000000..3de27beb07 --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/AveragePriceComparer.cs @@ -0,0 +1,45 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Protocol.Transaction; +using Nethermind.Dirichlet.Numerics; + +namespace Catalyst.Core.Modules.Consensus.Deltas.Building +{ + internal sealed class AveragePriceComparer : IComparer + { + private readonly int _multiplier; + + private AveragePriceComparer(int multiplier) { _multiplier = multiplier; } + + public int Compare(PublicEntry x, PublicEntry y) + { + return _multiplier * Comparer.Default.Compare(x?.GasPrice.ToUInt256(), y?.GasPrice.ToUInt256()); + } + + public static AveragePriceComparer InstanceDesc { get; } = new(-1); + public static AveragePriceComparer InstanceAsc { get; } = new(1); + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/Building/CandidateBuilderStep.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/CandidateBuilderStep.cs new file mode 100644 index 0000000000..7ddae6ab3e --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/CandidateBuilderStep.cs @@ -0,0 +1,131 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Hashing; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; +using Catalyst.Core.Modules.Kvm; +using Catalyst.Protocol.Transaction; +using Catalyst.Protocol.Wire; +using Google.Protobuf; +using Lib.P2P; +using MultiFormats; +using Nethermind.Dirichlet.Numerics; +using Serilog; + +namespace Catalyst.Core.Modules.Consensus.Deltas.Building +{ + internal sealed class CandidateBuilderStep : IDeltaBuilderStep + { + private readonly MultiAddress _producerUniqueId; + private readonly IDeterministicRandomFactory _randomFactory; + private readonly IHashProvider _hashProvider; + private readonly ILogger _logger; + + public CandidateBuilderStep(MultiAddress producerUniqueId, + IDeterministicRandomFactory randomFactory, + IHashProvider hashProvider, + ILogger logger) + { + _producerUniqueId = producerUniqueId ?? throw new ArgumentNullException(nameof(producerUniqueId)); + _randomFactory = randomFactory ?? throw new ArgumentNullException(nameof(randomFactory)); + _hashProvider = hashProvider ?? throw new ArgumentNullException(nameof(hashProvider)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + private byte[] GetSaltFromPreviousDelta(Cid previousDeltaHash) + { + IDeterministicRandom isaac = _randomFactory.GetDeterministicRandomFromSeed(previousDeltaHash.ToArray()); + return BitConverter.GetBytes(isaac.NextInt()); + } + + public void Execute(DeltaBuilderContext context) + { + byte[] salt = GetSaltFromPreviousDelta(context.PreviousDeltaHash); + + IEnumerable rawAndSaltedEntriesBySignature = context.Transactions.Select(e => new RawEntryWithSaltedAndHashedEntry(e, salt, _hashProvider)); + + // (Eα;Oα) + byte[] shuffledEntriesBytes = rawAndSaltedEntriesBySignature + .OrderBy(v => v.SaltedAndHashedEntry, ByteUtil.ByteListComparer.Default) + .SelectMany(v => v.RawEntry.ToByteArray()) + .ToArray(); + + // dn + byte[] signaturesInOrder = context.Transactions + .Select(p => p.Signature.ToByteArray()) + .OrderBy(s => s, ByteUtil.ByteListComparer.Default) + .SelectMany(b => b) + .ToArray(); + + // xf + UInt256 summedFees = context.Transactions.Sum(t => t.GasPrice.ToUInt256() * t.GasLimit); + + //∆Ln,j = L(f/E) + dn + E(xf, j) + context.CoinbaseEntry = new CoinbaseEntry + { + Amount = summedFees.ToUint256ByteString(), + ReceiverKvmAddress = _producerUniqueId.GetPublicKeyBytes().ToKvmAddressByteString() + }; + + byte[] globalLedgerStateUpdate = shuffledEntriesBytes + .Concat(signaturesInOrder) + .Concat(context.CoinbaseEntry.ToByteArray()) + .ToArray(); + + //hj + CandidateDeltaBroadcast candidate = new CandidateDeltaBroadcast + { + // h∆j + Hash = MultiBase.Decode(_hashProvider.ComputeMultiHash(globalLedgerStateUpdate).ToCid()) + .ToByteString(), + + // Idj + Producer = _producerUniqueId.GetPublicKeyBytes().ToKvmAddressByteString(), + PreviousDeltaDfsHash = context.PreviousDeltaHash.ToArray().ToByteString() + }; + + context.Candidate = candidate; + } + + private sealed class RawEntryWithSaltedAndHashedEntry + { + public PublicEntry RawEntry { get; } + + public byte[] SaltedAndHashedEntry { get; } + + public RawEntryWithSaltedAndHashedEntry(PublicEntry rawEntry, + byte[] salt, + IHashProvider hashProvider) + { + RawEntry = rawEntry; + SaltedAndHashedEntry = hashProvider.ComputeMultiHash(rawEntry, salt).ToArray(); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaBuilder.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaBuilder.cs new file mode 100644 index 0000000000..c7f735b7dd --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaBuilder.cs @@ -0,0 +1,119 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Config; +using Catalyst.Abstractions.Consensus; +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Kvm; +using Catalyst.Abstractions.P2P; +using Catalyst.Core.Modules.Dfs.Extensions; +using Catalyst.Protocol.Peer; +using Catalyst.Protocol.Wire; +using Google.Protobuf; +using Lib.P2P; +using MultiFormats; +using Nethermind.State; +using Serilog; + +namespace Catalyst.Core.Modules.Consensus.Deltas.Building +{ + /// + public sealed class DeltaBuilder : IDeltaBuilder + { + private readonly IDeltaTransactionRetriever _transactionRetriever; + private readonly IDeterministicRandomFactory _randomFactory; + private readonly IHashProvider _hashProvider; + private readonly MultiAddress _producerUniqueId; + private readonly IDeltaCache _deltaCache; + private readonly IDateTimeProvider _dateTimeProvider; + private readonly IStateProvider _stateProvider; + private readonly IDeltaExecutor _deltaExecutor; + private readonly IDeltaConfig _deltaConfig; + private readonly ITransactionConfig _transactionConfig; + private readonly ILogger _logger; + + public DeltaBuilder(IDeltaTransactionRetriever transactionRetriever, + IDeterministicRandomFactory randomFactory, + IHashProvider hashProvider, + IPeerSettings peerSettings, + IDeltaCache deltaCache, + IDateTimeProvider dateTimeProvider, + IStateProvider stateProvider, + IDeltaExecutor deltaExecutor, + IDeltaConfig deltaConfig, + ITransactionConfig transactionConfig, + ILogger logger) + { + _transactionRetriever = transactionRetriever; + _randomFactory = randomFactory; + _hashProvider = hashProvider; + _producerUniqueId = peerSettings.Address; + _deltaCache = deltaCache; + _dateTimeProvider = dateTimeProvider; + _stateProvider = stateProvider; + _deltaExecutor = deltaExecutor; + _deltaConfig = deltaConfig; + _transactionConfig = transactionConfig; + + _logger = logger; + + PrepareSteps(); + } + + //todo We can DI this later + private void PrepareSteps() + { + _steps = new IDeltaBuilderStep[] + { + new PreviousDeltaResolverStep(_deltaCache, _logger), + new TransactionRetrieverStep(_transactionRetriever, _deltaConfig, _transactionConfig, _logger), + new CandidateBuilderStep(_producerUniqueId, _randomFactory, _hashProvider, _logger), + new ProducedDeltaBuilderStep(_dateTimeProvider, _logger), + new DeltaStateCalculationStep(_stateProvider, _deltaExecutor, _logger), + new DeltaRegistrationStep(_deltaCache, _logger) + }; + } + + IDeltaBuilderStep[] _steps; + + /// + public CandidateDeltaBroadcast BuildCandidateDelta(Cid previousDeltaHash) + { + DeltaBuilderContext context = new(previousDeltaHash); + foreach (IDeltaBuilderStep step in _steps) + { + step.Execute(context); + } + + // to simplify cases when we test truffle + // if ((context.Transactions?.Count ?? 0) == 0) + // { + // return null; + // } + + return context.Candidate; + } + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaBuilderContext.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaBuilderContext.cs new file mode 100644 index 0000000000..4b2d37aa40 --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaBuilderContext.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Transaction; +using Catalyst.Protocol.Wire; +using Lib.P2P; + +namespace Catalyst.Core.Modules.Consensus.Deltas.Building +{ + internal sealed class DeltaBuilderContext + { + public DeltaBuilderContext(Cid previousDeltaHash) { PreviousDeltaHash = previousDeltaHash; } + + public Cid PreviousDeltaHash { get; } + public Delta PreviousDelta { get; set; } + public IList Transactions { get; set; } + public CandidateDeltaBroadcast Candidate { get; set; } + public CoinbaseEntry CoinbaseEntry { get; set; } + public Delta ProducedDelta { get; set; } + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaRegistrationStep.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaRegistrationStep.cs new file mode 100644 index 0000000000..041ec0ed1b --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaRegistrationStep.cs @@ -0,0 +1,54 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.Consensus.Deltas; +using Serilog; +using Serilog.Events; + +namespace Catalyst.Core.Modules.Consensus.Deltas.Building +{ + internal sealed class DeltaRegistrationStep : IDeltaBuilderStep + { + private readonly IDeltaCache _deltaCache; + private readonly ILogger _logger; + + public DeltaRegistrationStep(IDeltaCache deltaCache, ILogger logger) + { + _deltaCache = deltaCache ?? throw new ArgumentNullException(nameof(deltaCache)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public void Execute(DeltaBuilderContext context) + { + if (_logger.IsEnabled(LogEventLevel.Debug)) _logger.Debug("Registering new delta with parent ({previousHash})", context.PreviousDeltaHash); + _deltaCache.AddLocalDelta(context.Candidate, context.ProducedDelta); + + // for easier delta tracking when testing truffle + // if ((context.ProducedDelta.PublicEntries?.Count ?? 0) > 0) + // { + // _deltaCache.AddLocalDelta(context.Candidate, context.ProducedDelta); + // } + } + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaStateCalculationStep.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaStateCalculationStep.cs new file mode 100644 index 0000000000..40f3f0b1d6 --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/DeltaStateCalculationStep.cs @@ -0,0 +1,62 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.Kvm; +using Nethermind.Core.Crypto; +using Nethermind.Evm.Tracing; +using Nethermind.State; +using Serilog; +using Serilog.Events; + +namespace Catalyst.Core.Modules.Consensus.Deltas.Building +{ + internal sealed class DeltaStateCalculationStep : IDeltaBuilderStep + { + private readonly IStateProvider _stateProvider; + private readonly IDeltaExecutor _deltaExecutor; + private readonly ILogger _logger; + + public DeltaStateCalculationStep(IStateProvider stateProvider, IDeltaExecutor deltaExecutor, ILogger logger) + { + // note that this mus be a different state provider and a different executor + _stateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider)); + _deltaExecutor = deltaExecutor ?? throw new ArgumentNullException(nameof(deltaExecutor)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public void Execute(DeltaBuilderContext context) + { + var previousRoot = context.PreviousDelta.StateRoot; + Keccak stateRoot = previousRoot.IsEmpty ? Keccak.EmptyTreeHash : new Keccak(previousRoot.ToByteArray()); + + if (_logger.IsEnabled(LogEventLevel.Debug)) _logger.Error($"Running state calculation for delta {context.ProducedDelta.DeltaNumber}"); + + // here we need a read only delta executor (like in block builders - everything reverts in the end) + _stateProvider.StateRoot = stateRoot; + _deltaExecutor.CallAndReset(context.ProducedDelta, NullTxTracer.Instance); + + _stateProvider.Reset(); + } + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/Building/IDeltaBuilderStep.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/IDeltaBuilderStep.cs new file mode 100644 index 0000000000..f39491b414 --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/IDeltaBuilderStep.cs @@ -0,0 +1,30 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Consensus.Deltas.Building +{ + internal interface IDeltaBuilderStep + { + void Execute(DeltaBuilderContext context); + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/Building/PreviousDeltaResolverStep.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/PreviousDeltaResolverStep.cs new file mode 100644 index 0000000000..2486fff06b --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/PreviousDeltaResolverStep.cs @@ -0,0 +1,55 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Protocol.Deltas; +using Serilog; +using Serilog.Events; + +namespace Catalyst.Core.Modules.Consensus.Deltas.Building +{ + internal sealed class PreviousDeltaResolverStep : IDeltaBuilderStep + { + private readonly IDeltaCache _deltaCache; + private readonly ILogger _logger; + + public PreviousDeltaResolverStep(IDeltaCache deltaCache, ILogger logger) + { + _deltaCache = deltaCache ?? throw new ArgumentNullException(nameof(deltaCache)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public void Execute(DeltaBuilderContext context) + { + if (_logger.IsEnabled(LogEventLevel.Debug)) _logger.Debug("Resolving previous delta by hash ({previousHash})", context.PreviousDeltaHash); + if (!_deltaCache.TryGetOrAddConfirmedDelta(context.PreviousDeltaHash, out Delta previousDelta)) + { + throw new InvalidDataException("Cannot retrieve previous delta from cache during delta construction."); + } + + context.PreviousDelta = previousDelta; + } + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/Building/ProducedDeltaBuilderStep.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/ProducedDeltaBuilderStep.cs new file mode 100644 index 0000000000..f2f1d1fd29 --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/ProducedDeltaBuilderStep.cs @@ -0,0 +1,76 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.Consensus; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Modules.Kvm; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Transaction; +using Google.Protobuf.WellKnownTypes; +using Serilog; +using Serilog.Events; + +namespace Catalyst.Core.Modules.Consensus.Deltas.Building +{ + internal sealed class ProducedDeltaBuilderStep : IDeltaBuilderStep + { + private readonly IDateTimeProvider _dateTimeProvider; + private readonly ILogger _logger; + + public ProducedDeltaBuilderStep(IDateTimeProvider dateTimeProvider, ILogger logger) + { + _dateTimeProvider = dateTimeProvider ?? throw new ArgumentNullException(nameof(dateTimeProvider)); + _logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + public void Execute(DeltaBuilderContext context) + { + context.ProducedDelta = new Delta + { + PreviousDeltaDfsHash = context.PreviousDeltaHash.ToArray().ToByteString(), + DeltaNumber = context.PreviousDelta.DeltaNumber + 1, + MerkleRoot = context.Candidate.Hash, + CoinbaseEntries = + { + context.CoinbaseEntry + }, + + PublicEntries = + { + context.Transactions + }, + + GasUsed = (long)context.Transactions.Sum(x => x.GasLimit), + + TimeStamp = Timestamp.FromDateTime(_dateTimeProvider.UtcNow) + }; + + if (_logger.IsEnabled(LogEventLevel.Information)) _logger.Information("Built a delta {number} {hash} based on {previousHash} with {txCount} transactions", context.ProducedDelta.DeltaNumber, context.Candidate.Hash, context.ProducedDelta.PreviousDeltaDfsHash, context.ProducedDelta.PublicEntries.Count); + foreach (PublicEntry publicEntry in context.ProducedDelta.PublicEntries) + { + if (_logger.IsEnabled(LogEventLevel.Information)) _logger.Information("Entry {from} -> {to} with nonce {nonce}", publicEntry.SenderAddress.ToAddress(), publicEntry.ReceiverAddress.ToAddress(), publicEntry.Nonce); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/Building/TransactionRetrieverStep.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/TransactionRetrieverStep.cs new file mode 100644 index 0000000000..99235e0114 --- /dev/null +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/Building/TransactionRetrieverStep.cs @@ -0,0 +1,134 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using Catalyst.Abstractions.Config; +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Protocol.Transaction; +using Dawn; +using Serilog; + +namespace Catalyst.Core.Modules.Consensus.Deltas.Building +{ + internal sealed class TransactionRetrieverStep : IDeltaBuilderStep + { + private readonly ILogger _logger; + private readonly IDeltaTransactionRetriever _retriever; + private readonly IDeltaConfig _deltaConfig; + private readonly ITransactionConfig _transactionConfig; + + public TransactionRetrieverStep(IDeltaTransactionRetriever retriever, IDeltaConfig deltaConfig, ITransactionConfig transactionConfig, ILogger logger) + { + Guard.Argument(retriever, nameof(retriever)).NotNull(); + Guard.Argument(deltaConfig, nameof(deltaConfig)).NotNull(); + Guard.Argument(transactionConfig, nameof(transactionConfig)).NotNull(); + Guard.Argument(logger, nameof(logger)).NotNull(); + + _retriever = retriever; + _deltaConfig = deltaConfig; + _transactionConfig = transactionConfig; + _logger = logger; + } + + public void Execute(DeltaBuilderContext context) + { + IList allTransactions = _retriever.GetMempoolTransactionsByPriority(); + + Guard.Argument(allTransactions, nameof(allTransactions)) + .NotNull("Mempool content returned null, check the mempool is actively running"); + + IList includedTransactions = GetValidTransactionsForDelta(allTransactions); + context.Transactions = includedTransactions; + } + + private static bool IsTransactionOfAcceptedType(PublicEntry transaction) { return transaction.IsPublicTransaction || transaction.IsContractCall || transaction.IsContractDeployment; } + + /// + /// Gets the valid transactions for delta. + /// This method can be used to extract the collection of transactions that meet the criteria for validating delta. + /// + private IList GetValidTransactionsForDelta(IList allTransactions) + { + //lock time equals 0 or less than ledger cycle time + //we assume all transactions are of type non-confidential for now + + List validTransactionsForDelta = new(); + List rejectedTransactions = new(); + + int allTransactionsCount = allTransactions.Count; + for (int i = 0; i < allTransactionsCount; i++) + { + PublicEntry currentItem = allTransactions[i]; + if (!IsTransactionOfAcceptedType(currentItem)) + { + rejectedTransactions.Add(currentItem); + continue; + } + + validTransactionsForDelta.Add(currentItem); + } + + validTransactionsForDelta.Sort(AveragePriceComparer.InstanceDesc); + + ulong totalLimit = 0UL; + int allValidCount = validTransactionsForDelta.Count; + int rejectedCountBeforeLimitChecks = rejectedTransactions.Count; + for (int i = 0; i < allValidCount; i++) + { + PublicEntry currentItem = validTransactionsForDelta[i]; + ulong remainingLimit = _deltaConfig.DeltaGasLimit - totalLimit; + if (remainingLimit < _transactionConfig.MinTransactionEntryGasLimit) + { + for (int j = i; j < allValidCount; j++) + { + currentItem = validTransactionsForDelta[j]; + rejectedTransactions.Add(currentItem); + } + + break; + } + + ulong currentItemGasLimit = currentItem.GasLimit; + if (remainingLimit < currentItemGasLimit) + { + rejectedTransactions.Add(currentItem); + } + else + { + totalLimit += validTransactionsForDelta[i].GasLimit; + } + } + + for (int i = rejectedCountBeforeLimitChecks; i < rejectedTransactions.Count; i++) + { + validTransactionsForDelta.Remove(rejectedTransactions[i]); + } + + _logger.Debug("Delta builder rejected the following transactions {rejectedTransactions}", + rejectedTransactions); + + return validTransactionsForDelta; + } + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaBuilder.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaBuilder.cs index 2c0f2fbb82..2ddb460533 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaBuilder.cs +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaBuilder.cs @@ -30,8 +30,9 @@ using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.P2P; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Extensions.Protocol.Wire; +using Catalyst.Core.Lib.Service; using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Protocol.Deltas; using Catalyst.Protocol.Peer; using Catalyst.Protocol.Transaction; @@ -39,10 +40,10 @@ using Dawn; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; -using LibP2P; +using Lib.P2P; +using MultiFormats; using Nethermind.Dirichlet.Numerics; using Serilog; -using TheDotNetLeague.MultiFormats.MultiBase; namespace Catalyst.Core.Modules.Consensus.Deltas { @@ -51,13 +52,14 @@ public sealed class DeltaBuilder : IDeltaBuilder { private const ulong DeltaGasLimit = 8_000_000; private const ulong MinTransactionEntryGasLimit = 21_000; - + private readonly IDeltaTransactionRetriever _transactionRetriever; private readonly IDeterministicRandomFactory _randomFactory; private readonly IHashProvider _hashProvider; private readonly PeerId _producerUniqueId; private readonly IDeltaCache _deltaCache; private readonly IDateTimeProvider _dateTimeProvider; + private readonly IDeltaIndexService _deltaIndexService; private readonly ILogger _logger; public DeltaBuilder(IDeltaTransactionRetriever transactionRetriever, @@ -66,8 +68,10 @@ public DeltaBuilder(IDeltaTransactionRetriever transactionRetriever, IPeerSettings peerSettings, IDeltaCache deltaCache, IDateTimeProvider dateTimeProvider, + IDeltaIndexService deltaIndexService, ILogger logger) { + _deltaIndexService = deltaIndexService; _transactionRetriever = transactionRetriever; _randomFactory = randomFactory; _hashProvider = hashProvider; @@ -80,6 +84,8 @@ public DeltaBuilder(IDeltaTransactionRetriever transactionRetriever, /// public CandidateDeltaBroadcast BuildCandidateDelta(Cid previousDeltaHash) { + _logger.Error("Current height: " + _deltaIndexService.Height()); + _logger.Debug("Building candidate delta locally"); var allTransactions = _transactionRetriever.GetMempoolTransactionsByPriority(); @@ -90,21 +96,8 @@ public CandidateDeltaBroadcast BuildCandidateDelta(Cid previousDeltaHash) var includedTransactions = GetValidTransactionsForDelta(allTransactions); var salt = GetSaltFromPreviousDelta(previousDeltaHash); - var rawAndSaltedEntriesBySignature = includedTransactions.SelectMany( - t => t.PublicEntries.Select(e => - new RawEntryWithSaltedAndHashedEntry(e, salt, _hashProvider))); - - var rawAndSaltedContractEntriesBySignature = includedTransactions.SelectMany( - t => t.ContractEntries.Select(e => - { - var contractEntriesProtoBuff = e; - return new - { - RawEntry = contractEntriesProtoBuff, - SaltedAndHashedEntry = - _hashProvider.ComputeMultiHash(contractEntriesProtoBuff.ToByteArray().Concat(salt)) - }; - })).ToArray(); + var rawAndSaltedEntriesBySignature = includedTransactions.Select( + x => new RawEntryWithSaltedAndHashedEntry(x, salt, _hashProvider)); // (Eα;Oα) var shuffledEntriesBytes = rawAndSaltedEntriesBySignature @@ -112,11 +105,6 @@ public CandidateDeltaBroadcast BuildCandidateDelta(Cid previousDeltaHash) .SelectMany(v => v.RawEntry.ToByteArray()) .ToArray(); - var shuffledContractEntriesBytes = rawAndSaltedContractEntriesBySignature - .OrderBy(v => v.SaltedAndHashedEntry.ToArray(), ByteUtil.ByteListComparer.Default) - .SelectMany(v => v.RawEntry.ToByteArray()) - .ToArray(); - // dn var signaturesInOrder = includedTransactions .Select(p => p.Signature.ToByteArray()) @@ -125,7 +113,7 @@ public CandidateDeltaBroadcast BuildCandidateDelta(Cid previousDeltaHash) .ToArray(); // xf - var summedFees = includedTransactions.Sum(t => t.SummedEntryFees()); + var summedFees = includedTransactions.Sum(t => t.TransactionFees.ToUInt256()); //∆Ln,j = L(f/E) + dn + E(xf, j) var coinbaseEntry = new CoinbaseEntry @@ -134,7 +122,6 @@ public CandidateDeltaBroadcast BuildCandidateDelta(Cid previousDeltaHash) ReceiverPublicKey = _producerUniqueId.PublicKey.ToByteString() }; var globalLedgerStateUpdate = shuffledEntriesBytes - .Concat(shuffledContractEntriesBytes) .Concat(signaturesInOrder) .Concat(coinbaseEntry.ToByteArray()) .ToArray(); @@ -143,7 +130,7 @@ public CandidateDeltaBroadcast BuildCandidateDelta(Cid previousDeltaHash) var candidate = new CandidateDeltaBroadcast { // h∆j - Hash = MultiBase.Decode(CidHelper.CreateCid(_hashProvider.ComputeMultiHash(globalLedgerStateUpdate))) + Hash = MultiBase.Decode(_hashProvider.ComputeMultiHash(globalLedgerStateUpdate).ToCid()) .ToByteString(), // Idj @@ -158,8 +145,7 @@ public CandidateDeltaBroadcast BuildCandidateDelta(Cid previousDeltaHash) PreviousDeltaDfsHash = previousDeltaHash.ToArray().ToByteString(), MerkleRoot = candidate.Hash, CoinbaseEntries = {coinbaseEntry}, - PublicEntries = {includedTransactions.SelectMany(t => t.PublicEntries).Select(x => x)}, - ContractEntries = {includedTransactions.SelectMany(t => t.ContractEntries).Select(x => x)}, + PublicEntries = {includedTransactions}, TimeStamp = Timestamp.FromDateTime(_dateTimeProvider.UtcNow) }; @@ -170,7 +156,7 @@ public CandidateDeltaBroadcast BuildCandidateDelta(Cid previousDeltaHash) return candidate; } - private IEnumerable GetSaltFromPreviousDelta(Cid previousDeltaHash) + private byte[] GetSaltFromPreviousDelta(Cid previousDeltaHash) { var isaac = _randomFactory.GetDeterministicRandomFromSeed(previousDeltaHash.ToArray()); return BitConverter.GetBytes(isaac.NextInt()); @@ -183,48 +169,51 @@ private sealed class RawEntryWithSaltedAndHashedEntry public byte[] SaltedAndHashedEntry { get; } public RawEntryWithSaltedAndHashedEntry(PublicEntry rawEntry, - IEnumerable salt, + byte[] salt, IHashProvider hashProvider) { RawEntry = rawEntry; - SaltedAndHashedEntry = hashProvider.ComputeMultiHash(rawEntry.ToByteArray().Concat(salt)).ToArray(); + SaltedAndHashedEntry = hashProvider.ComputeMultiHash(rawEntry, salt).ToArray(); } } - private sealed class AveragePriceComparer : IComparer + private sealed class AveragePriceComparer : IComparer { private readonly int _multiplier; private AveragePriceComparer(int multiplier) { _multiplier = multiplier; } - - public int Compare(TransactionBroadcast x, TransactionBroadcast y) + + public int Compare(PublicEntry x, PublicEntry y) { - return _multiplier * Comparer.Default.Compare(x?.AverageGasPrice, y?.AverageGasPrice); + return _multiplier * Comparer.Default.Compare(x?.GasPrice.ToUInt256(), y?.GasPrice.ToUInt256()); } - + public static AveragePriceComparer InstanceDesc { get; } = new AveragePriceComparer(-1); public static AveragePriceComparer InstanceAsc { get; } = new AveragePriceComparer(1); } - - private static bool IsTransactionOfAcceptedType(TransactionBroadcast transaction) { return transaction.IsPublicTransaction || transaction.IsContractCall || transaction.IsContractDeployment; } - + + private static bool IsTransactionOfAcceptedType(PublicEntry transaction) + { + return transaction.IsPublicTransaction || transaction.IsContractCall || transaction.IsContractDeployment; + } + /// /// Gets the valid transactions for delta. /// This method can be used to extract the collection of transactions that meet the criteria for validating delta. /// - private IList GetValidTransactionsForDelta(IList allTransactions) + private IList GetValidTransactionsForDelta(IList allTransactions) { //lock time equals 0 or less than ledger cycle time //we assume all transactions are of type non-confidential for now - var validTransactionsForDelta = new List(); - var rejectedTransactions = new List(); + var validTransactionsForDelta = new List(); + var rejectedTransactions = new List(); var allTransactionsCount = allTransactions.Count; for (var i = 0; i < allTransactionsCount; i++) { var currentItem = allTransactions[i]; - if (!IsTransactionOfAcceptedType(currentItem) || !currentItem.HasValidEntries()) + if (!IsTransactionOfAcceptedType(currentItem)) { rejectedTransactions.Add(currentItem); continue; @@ -232,7 +221,7 @@ private IList GetValidTransactionsForDelta(IList GetValidTransactionsForDelta(IList GetValidTransactionsForDelta(IList * @@ -25,14 +25,22 @@ using System.Threading; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Hashing; +using Catalyst.Core.Lib.Service; +using Catalyst.Core.Modules.Dfs.Extensions; +using Catalyst.Core.Modules.Kvm; using Catalyst.Protocol.Deltas; using Catalyst.Protocol.Wire; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; -using LibP2P; +using Lib.P2P; using Microsoft.Extensions.Caching.Memory; +using Nethermind.Core; +using MultiFormats; +using Nethermind.Db; +using Nethermind.State; using Serilog; -using TheDotNetLeague.MultiFormats.MultiBase; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; namespace Catalyst.Core.Modules.Consensus.Deltas { @@ -42,25 +50,29 @@ public class DeltaCache : IDeltaCache, IDisposable { private readonly IMemoryCache _memoryCache; private readonly IDeltaDfsReader _dfsReader; + private readonly IDeltaIndexService _deltaIndexService; private readonly ILogger _logger; private readonly Func _entryOptions; public Cid GenesisHash { get; set; } - public static string GetLocalDeltaCacheKey(CandidateDeltaBroadcast candidate) - { - return nameof(DeltaCache) + "-LocalDelta-" + MultiBase.Encode(candidate.Hash.ToByteArray(), "base32"); - } + public static string GetLocalDeltaCacheKey(CandidateDeltaBroadcast candidate) { return nameof(DeltaCache) + "-LocalDelta-" + MultiBase.Encode(candidate.Hash.ToByteArray(), "base32"); } + + public static Address TruffleTestAccount = new("0xb77aec9f59f9d6f39793289a09aea871932619ed"); + + public static Address CatalystTruffleTestAccount = new("0x58BeB247771F0B6f87AA099af479aF767CcC0F00"); public DeltaCache(IHashProvider hashProvider, IMemoryCache memoryCache, IDeltaDfsReader dfsReader, IDeltaCacheChangeTokenProvider changeTokenProvider, + IStorageProvider storageProvider, + IStateProvider stateProvider, + ISnapshotableDb stateDb, + ISnapshotableDb codeDb, + IDeltaIndexService deltaIndexService, ILogger logger) { - var genesisDelta = new Delta {TimeStamp = Timestamp.FromDateTime(DateTime.MinValue.ToUniversalTime())}; - - GenesisHash = hashProvider.ComputeMultiHash(new Delta().ToByteArray()); - + _deltaIndexService = deltaIndexService; _dfsReader = dfsReader; _logger = logger; _entryOptions = () => new MemoryCacheEntryOptions() @@ -68,13 +80,30 @@ public DeltaCache(IHashProvider hashProvider, .RegisterPostEvictionCallback(EvictionCallback); _memoryCache = memoryCache; + + stateProvider.CreateAccount(TruffleTestAccount, 1_000_000_000.Kat()); + stateProvider.CreateAccount(CatalystTruffleTestAccount, 1_000_000_000.Kat()); + + storageProvider.Commit(); + stateProvider.Commit(CatalystGenesisSpec.Instance); + + storageProvider.CommitTrees(); + stateProvider.CommitTree(); + + stateDb.Commit(); + codeDb.Commit(); + + var genesisDelta = new Delta + { + TimeStamp = Timestamp.FromDateTime(DateTime.UnixEpoch), + StateRoot = ByteString.CopyFrom(stateProvider.StateRoot.Bytes), + }; + + GenesisHash = hashProvider.ComputeMultiHash(genesisDelta).ToCid(); _memoryCache.Set(GenesisHash, genesisDelta); } - private void EvictionCallback(object key, object value, EvictionReason reason, object state) - { - _logger.Debug("Evicted Delta {0} from cache.", key); - } + private void EvictionCallback(object key, object value, EvictionReason reason, object state) { _logger.Debug("Evicted Delta {0} from cache.", key); } /// public bool TryGetOrAddConfirmedDelta(Cid cid, @@ -103,6 +132,16 @@ public bool TryGetLocalDelta(CandidateDeltaBroadcast candidate, out Delta delta) return tryGetLocalDelta; } + public void AddLocalDelta(Cid cid, Delta delta) + { + _logger.Verbose("Adding local details of delta with CID {cid}", cid); + _memoryCache.Set(cid, delta, _entryOptions()); + if (!TryGetOrAddConfirmedDelta(cid, out Delta retrieved, CancellationToken.None)) + { + throw new InvalidOperationException(); + } + } + public void AddLocalDelta(CandidateDeltaBroadcast localCandidate, Delta delta) { _logger.Verbose("Adding full details of candidate delta {candidate}", localCandidate); diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaCacheChangeTokenProvider.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaCacheChangeTokenProvider.cs index 10ff90b16e..c6d20bacaf 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaCacheChangeTokenProvider.cs +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaCacheChangeTokenProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaElector.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaElector.cs index aa9059acab..d86c96d216 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaElector.cs +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaElector.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,12 +27,17 @@ using System.Linq; using System.Threading; using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Types; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.P2P.ReputationSystem; using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Protocol.Wire; using Dawn; -using LibP2P; +using Lib.P2P; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Primitives; +using Nethermind.Core; using Serilog; namespace Catalyst.Core.Modules.Consensus.Deltas @@ -42,13 +47,14 @@ public class DeltaElector : IDeltaElector { private readonly IMemoryCache _candidatesCache; private readonly IDeltaProducersProvider _deltaProducersProvider; + private readonly IReputationManager _reputationManager; private readonly ILogger _logger; private readonly Func _cacheEntryOptions; public static string GetCandidateListCacheKey(FavouriteDeltaBroadcast candidate) { return nameof(DeltaElector) + "-" + - CidHelper.Cast(candidate.Candidate.PreviousDeltaDfsHash.ToByteArray()); + candidate.Candidate.PreviousDeltaDfsHash.ToByteArray().ToCid(); } public string GetCandidateListCacheKey(Cid previousDeltaHash) @@ -58,10 +64,12 @@ public string GetCandidateListCacheKey(Cid previousDeltaHash) public DeltaElector(IMemoryCache candidatesCache, IDeltaProducersProvider deltaProducersProvider, + IReputationManager reputationManager, ILogger logger) { _candidatesCache = candidatesCache; _deltaProducersProvider = deltaProducersProvider; + _reputationManager = reputationManager; _cacheEntryOptions = () => new MemoryCacheEntryOptions() .AddExpirationToken( new CancellationChangeToken(new CancellationTokenSource(TimeSpan.FromMinutes(3)).Token)); @@ -83,15 +91,18 @@ public void OnNext(FavouriteDeltaBroadcast candidate) { Guard.Argument(candidate, nameof(candidate)).NotNull().Require(f => f.IsValid()); - var cid = CidHelper.Cast(candidate.Candidate.PreviousDeltaDfsHash.ToByteArray()); + var cid = candidate.Candidate.PreviousDeltaDfsHash.ToByteArray().ToCid(); + Address candidateAddress = new(candidate.Voter.ToByteArray()); if (!_deltaProducersProvider .GetDeltaProducersFromPreviousDelta(cid) - .Any(p => p.Equals(candidate.VoterId))) + .Any(p => p.Equals(candidateAddress))) { - //https://github.com/catalyst-network/Catalyst.Node/issues/827 + ReputationChange reputationChange = new(new Address(candidate.Voter.ToByteArray()), ReputationEventType.VoterIsNotProducer); + _reputationManager.OnNext(reputationChange); + _logger.Debug( "Voter {voter} is not a producer for this cycle succeeding {deltaHash} and its vote has been discarded.", - candidate.VoterId, candidate.Candidate.PreviousDeltaDfsHash); + candidate.Voter, candidate.Candidate.PreviousDeltaDfsHash); return; } diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaHashProvider.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaHashProvider.cs index 01992e0900..d0e5f102cb 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaHashProvider.cs +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaHashProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,8 +28,9 @@ using System.Reactive.Subjects; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.Service; using Google.Protobuf.WellKnownTypes; -using LibP2P; +using Lib.P2P; using Nito.Comparers; using Serilog; @@ -48,6 +49,7 @@ public class DeltaHashProvider : IDeltaHashProvider public IObservable DeltaHashUpdates => _deltaHashUpdatesSubject.AsObservable(); public DeltaHashProvider(IDeltaCache deltaCache, + IDeltaIndexService deltaIndexService, ILogger logger, int capacity = 10_000) { @@ -61,35 +63,62 @@ public DeltaHashProvider(IDeltaCache deltaCache, Capacity = _capacity }; - _hashesByTimeDescending.Add(Timestamp.FromDateTime(DateTime.MinValue.ToUniversalTime()), - _deltaCache.GenesisHash); + var latestDeltaIndex = deltaIndexService.LatestDeltaIndex(); + if (deltaIndexService.LatestDeltaIndex() != null) + { + var foundDelta = _deltaCache.TryGetOrAddConfirmedDelta(latestDeltaIndex.Cid, out var delta); + _hashesByTimeDescending.Add(delta.TimeStamp, latestDeltaIndex.Cid); + return; + } + + _hashesByTimeDescending.Add(Timestamp.FromDateTime(DateTime.UnixEpoch), _deltaCache.GenesisHash); } /// public bool TryUpdateLatestHash(Cid previousHash, Cid newHash) { - var newAddress = newHash; - var previousAddress = previousHash; _logger.Debug("New hash {hash} received for previous hash {previousHash}", - newAddress, previousAddress); - var foundNewDelta = _deltaCache.TryGetOrAddConfirmedDelta(newAddress, out var newDelta); - var foundPreviousDelta = _deltaCache.TryGetOrAddConfirmedDelta(previousAddress, out var previousDelta); - - if (!foundNewDelta - || !foundPreviousDelta - || newDelta.PreviousDeltaDfsHash != previousHash.ToArray().ToByteString() - || previousDelta.TimeStamp >= newDelta.TimeStamp) + newHash, previousHash); + var foundNewDelta = _deltaCache.TryGetOrAddConfirmedDelta(newHash, out var newDelta); + var foundPreviousDelta = _deltaCache.TryGetOrAddConfirmedDelta(previousHash, out var previousDelta); + + if (!foundPreviousDelta) + { + _logger.Warning("Failed to update latest hash from {previousHash} to {newHash} due to previous delta not found", + previousHash, newHash); + return false; + } + + if (!foundNewDelta) { - _logger.Warning("Failed to update latest hash from {previousHash} to {newHash}", - previousAddress, newAddress); + _logger.Warning("Failed to update latest hash from {previousHash} to {newHash} due to new delta not found", previousHash, newHash); + return false; + } + + if (newDelta.PreviousDeltaDfsHash != previousHash.ToArray().ToByteString()) + { + _logger.Warning("Failed to update latest hash from {previousHash} to {newHash} due to new delta not being a childe of the previous one", + previousHash, newHash); + return false; + } + + if (previousDelta.TimeStamp >= newDelta.TimeStamp) + { + _logger.Warning("Failed to update latest hash from {previousHash} to {newHash} due to new delta being older {newTimestamp} than the previous one {oldTimestamp}", + previousHash, newHash, newDelta.TimeStamp, previousDelta.TimeStamp); return false; } _logger.Debug("Successfully to updated latest hash from {previousHash} to {newHash}", - previousAddress, newAddress); + previousHash, newHash); lock (_hashesByTimeDescending) { + if (_hashesByTimeDescending.ContainsValue(newHash)) + { + return false; + } + _hashesByTimeDescending.Add(newDelta.TimeStamp, newHash); if (_hashesByTimeDescending.Count > _capacity) { @@ -97,7 +126,7 @@ public bool TryUpdateLatestHash(Cid previousHash, Cid newHash) } } - _deltaHashUpdatesSubject.OnNext(newAddress); + _deltaHashUpdatesSubject.OnNext(newHash); return true; } diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaHub.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaHub.cs index a455bf37f8..e8c8da88da 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaHub.cs +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaHub.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,21 +23,23 @@ using System; using System.IO; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Options; using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Protocol.Deltas; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Dawn; using Google.Protobuf; -using LibP2P; +using Lib.P2P; +using MultiFormats; using Polly; using Polly.Retry; using Serilog; @@ -48,24 +50,27 @@ namespace Catalyst.Core.Modules.Consensus.Deltas /// public class DeltaHub : IDeltaHub { - private readonly IBroadcastManager _broadcastManager; - private readonly PeerId _peerId; - private readonly IDfs _dfs; + private readonly MultiAddress _peerId; + private readonly IDfsService _dfsService; + private readonly IHashProvider _hashProvider; + private readonly IPeerClient _peerClient; private readonly ILogger _logger; - protected virtual AsyncRetryPolicy DfsRetryPolicy { get; } + protected virtual AsyncRetryPolicy DfsRetryPolicy { get; } - public DeltaHub(IBroadcastManager broadcastManager, + public DeltaHub(IPeerClient peerClient, IPeerSettings peerSettings, - IDfs dfs, + IDfsService dfsService, + IHashProvider hashProvider, ILogger logger) { - _broadcastManager = broadcastManager; - _peerId = peerSettings.PeerId; - _dfs = dfs; + _peerId = peerSettings.Address; + _dfsService = dfsService; + _hashProvider = hashProvider; + _peerClient = peerClient; _logger = logger; - DfsRetryPolicy = Polly.Policy.Handle() + DfsRetryPolicy = Polly.Policy.Handle() .WaitAndRetryAsync(4, retryAttempt => TimeSpan.FromSeconds(Math.Pow(2, retryAttempt))); } @@ -76,7 +81,7 @@ public void BroadcastCandidate(CandidateDeltaBroadcast candidate) Guard.Argument(candidate, nameof(candidate)).NotNull().Require(c => c.IsValid()); _logger.Information("Broadcasting candidate delta {0}", candidate); - if (!candidate.ProducerId.Equals(_peerId)) + if (!candidate.Producer.SequenceEqual(_peerId.GetKvmAddressByteString())) { _logger.Warning($"{nameof(BroadcastCandidate)} " + "should only be called by the producer of a candidate."); @@ -84,7 +89,7 @@ public void BroadcastCandidate(CandidateDeltaBroadcast candidate) } var protocolMessage = candidate.ToProtocolMessage(_peerId, CorrelationId.GenerateCorrelationId()); - _broadcastManager.BroadcastAsync(protocolMessage).ConfigureAwait(false); + _peerClient.BroadcastAsync(protocolMessage).ConfigureAwait(false); _logger.Debug("Broadcast candidate {0} done.", candidate); } @@ -93,9 +98,9 @@ public void BroadcastCandidate(CandidateDeltaBroadcast candidate) public void BroadcastFavouriteCandidateDelta(FavouriteDeltaBroadcast favourite) { Guard.Argument(favourite, nameof(favourite)).NotNull().Require(c => c.IsValid()); - + var protocolMessage = favourite.ToProtocolMessage(_peerId, CorrelationId.GenerateCorrelationId()); - _broadcastManager.BroadcastAsync(protocolMessage).ConfigureAwait(false); + _peerClient.BroadcastAsync(protocolMessage).ConfigureAwait(false); _logger.Debug("Started broadcasting favourite candidate {0}", favourite); } @@ -105,11 +110,11 @@ public async Task PublishDeltaToDfsAndBroadcastAddressAsync(Delta delta, CancellationToken cancellationToken = default) { var newAddress = await PublishDeltaToDfs(delta, cancellationToken).ConfigureAwait(false); - await BroadcastNewDfsFileAddressAsync(newAddress, delta.PreviousDeltaDfsHash).ConfigureAwait(false); - return newAddress; + await BroadcastNewDfsFileAddressAsync(newAddress.Id, delta.PreviousDeltaDfsHash).ConfigureAwait(false); + return newAddress.Id; } - private async Task PublishDeltaToDfs(Delta delta, CancellationToken cancellationToken) + private async Task PublishDeltaToDfs(Delta delta, CancellationToken cancellationToken) { try { @@ -139,7 +144,7 @@ private async Task BroadcastNewDfsFileAddressAsync(Cid dfsFileAddress, ByteStrin { _logger.Verbose( "Broadcasting new delta dfs address {dfsAddress} for delta with previous delta hash {previousDeltaHash}", - dfsFileAddress, CidHelper.Cast(previousDeltaHash.ToByteArray())); + dfsFileAddress, previousDeltaHash.ToByteArray().ToCid()); var newDeltaHashOnDfs = new DeltaDfsHashBroadcast { @@ -147,7 +152,7 @@ private async Task BroadcastNewDfsFileAddressAsync(Cid dfsFileAddress, ByteStrin PreviousDeltaDfsHash = previousDeltaHash }.ToProtocolMessage(_peerId, CorrelationId.GenerateCorrelationId()); - await _broadcastManager.BroadcastAsync(newDeltaHashOnDfs).ConfigureAwait(false); + await _peerClient.BroadcastAsync(newDeltaHashOnDfs).ConfigureAwait(false); } catch (Exception exception) { @@ -155,16 +160,21 @@ private async Task BroadcastNewDfsFileAddressAsync(Cid dfsFileAddress, ByteStrin } } - private async Task PublishDfsFileAsync(byte[] deltaAsBytes, CancellationToken cancellationToken) + private async Task PublishDfsFileAsync(byte[] deltaAsBytes, + CancellationToken cancellationToken) { - using (var memoryStream = new MemoryStream()) + await using (MemoryStream memoryStream = new()) { await memoryStream.WriteAsync(deltaAsBytes, cancellationToken).ConfigureAwait(false); await memoryStream.FlushAsync(cancellationToken).ConfigureAwait(false); - + memoryStream.Seek(0, SeekOrigin.Begin); - return await _dfs.AddAsync(memoryStream, cancellationToken: cancellationToken).ConfigureAwait(false); + var node = await _dfsService.UnixFsApi.AddAsync(memoryStream, string.Empty, + new AddFileOptions { Hash = _hashProvider.HashingAlgorithm.Name }, cancellationToken) + .ConfigureAwait(false); + + return node; } } } diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaProducersProvider.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaProducersProvider.cs index 9fe60bd693..216f99e35e 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaProducersProvider.cs +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaProducersProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,9 +23,9 @@ using System; using System.Collections.Generic; -using Catalyst.Core.Lib.P2P.Repository; -using Catalyst.Protocol.Peer; -using LibP2P; +using Catalyst.Abstractions.P2P.Repository; +using Lib.P2P; +using Nethermind.Core; namespace Catalyst.Core.Modules.Consensus.Deltas { @@ -33,7 +33,7 @@ public sealed class DeltaProducersProvider : IDeltaProducersProvider { public DeltaProducersProvider(IPeerRepository peerRepository) { PeerRepository = peerRepository; } - public IList GetDeltaProducersFromPreviousDelta(Cid previousDeltaHash) + public IList
GetDeltaProducersFromPreviousDelta(Cid previousDeltaHash) { throw new NotImplementedException(); } diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaTransactionRetriever.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaTransactionRetriever.cs index 551222cd49..f23c02c122 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaTransactionRetriever.cs +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaTransactionRetriever.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,8 @@ using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Mempool; using Catalyst.Core.Lib.DAO; -using Catalyst.Protocol.Wire; +using Catalyst.Core.Lib.DAO.Transaction; +using Catalyst.Protocol.Transaction; using Dawn; namespace Catalyst.Core.Modules.Consensus.Deltas @@ -35,13 +36,13 @@ namespace Catalyst.Core.Modules.Consensus.Deltas /// public class DeltaTransactionRetriever : IDeltaTransactionRetriever { - private readonly IMempool _mempool; + private readonly IMempool _mempool; private readonly IMapperProvider _mapperProvider; /// public ITransactionComparer TransactionComparer { get; } - public DeltaTransactionRetriever(IMempool mempool, + public DeltaTransactionRetriever(IMempool mempool, IMapperProvider mapperProvider, ITransactionComparer transactionComparer) { @@ -51,11 +52,11 @@ public DeltaTransactionRetriever(IMempool mempool, } /// - public IList GetMempoolTransactionsByPriority(int maxCount = 2147483647) + public IList GetMempoolTransactionsByPriority(int maxCount = 2147483647) { Guard.Argument(maxCount, nameof(maxCount)).NotNegative().NotZero(); - var allTransactions = _mempool.Repository.GetAll().Select(x => x.ToProtoBuff(_mapperProvider)); + var allTransactions = _mempool.Service.GetAll().Select(x => x.ToProtoBuff(_mapperProvider)); var mempoolPrioritised = allTransactions.OrderByDescending(t => t, TransactionComparer) .Take(maxCount).Select(t => t).ToList(); diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaVoter.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaVoter.cs index 642a703542..be7289bbdb 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaVoter.cs +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaVoter.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,15 +28,18 @@ using System.Threading; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.P2P; +using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Dawn; -using LibP2P; +using Lib.P2P; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Primitives; +using MultiFormats; +using Nethermind.Core; using Serilog; -using TheDotNetLeague.MultiFormats.MultiBase; namespace Catalyst.Core.Modules.Consensus.Deltas { @@ -44,12 +47,12 @@ public class DeltaVoter : IDeltaVoter { public static string GetCandidateCacheKey(CandidateDeltaBroadcast candidate) { - return nameof(DeltaVoter) + "-" + CidHelper.Cast(candidate.Hash.ToByteArray()); + return nameof(DeltaVoter) + "-" + candidate.PreviousDeltaDfsHash.ToByteArray().ToCid() + "-" + candidate.Hash.ToByteArray().ToCid(); } public static string GetCandidateListCacheKey(CandidateDeltaBroadcast candidate) { - return nameof(DeltaVoter) + "-" + CidHelper.Cast(candidate.PreviousDeltaDfsHash.ToByteArray()); + return nameof(DeltaVoter) + "-" + candidate.PreviousDeltaDfsHash.ToByteArray().ToCid(); } public static string GetCandidateListCacheKey(Cid previousDeltaHash) @@ -64,7 +67,7 @@ public static string GetCandidateListCacheKey(Cid previousDeltaHash) private readonly IMemoryCache _candidatesCache; private readonly IDeltaProducersProvider _deltaProducersProvider; - private readonly PeerId _localPeerIdentifier; + private readonly MultiAddress _localPeerIdentifier; private readonly ILogger _logger; private readonly Func _cacheEntryOptions; @@ -75,7 +78,7 @@ public DeltaVoter(IMemoryCache candidatesCache, { _candidatesCache = candidatesCache; _deltaProducersProvider = deltaProducersProvider; - _localPeerIdentifier = peerSettings.PeerId; + _localPeerIdentifier = peerSettings.Address; _cacheEntryOptions = () => new MemoryCacheEntryOptions() .AddExpirationToken( new CancellationChangeToken(new CancellationTokenSource(TimeSpan.FromMinutes(3)).Token)); @@ -94,10 +97,8 @@ public void OnNext(CandidateDeltaBroadcast candidate) try { var rankingFactor = GetProducerRankFactor(candidate); - var candidateCacheKey = GetCandidateCacheKey(candidate); - if (_candidatesCache.TryGetValue(candidateCacheKey, out var retrievedScoredDelta) - ) + if (_candidatesCache.TryGetValue(candidateCacheKey, out var retrievedScoredDelta)) { retrievedScoredDelta.IncreasePopularity(1); _logger.Debug("Candidate {candidate} increased popularity to {score}", candidate, @@ -121,8 +122,8 @@ private void AddCandidateToCandidateHashLookup(CandidateDeltaBroadcast candidate var scoredDelta = new ScoredCandidateDelta(candidate, 100 * rankingFactor + 1); _candidatesCache.Set(candidateCacheKey, scoredDelta, _cacheEntryOptions()); _logger.Verbose("Candidate {hash} with previous hash {previousHash} has score {scored}", - candidate.Hash.ToByteArray().ToBase32(), - candidate.PreviousDeltaDfsHash.ToByteArray().ToBase32(), + candidate.Hash.ToByteArray().ToCid(), + candidate.PreviousDeltaDfsHash.ToByteArray().ToCid(), scoredDelta.Score); } @@ -137,7 +138,7 @@ private void AddCandidateToPreviousHashLookup(CandidateDeltaBroadcast candidate, }); candidatesForPreviousHash.Add(candidateCacheKey); _logger.Verbose("Candidates for previous hash {previousHash} are {candidates}", - candidate.PreviousDeltaDfsHash.ToByteArray().ToBase32(), candidatesForPreviousHash); + candidate.PreviousDeltaDfsHash.ToByteArray().ToCid(), candidatesForPreviousHash); } public bool TryGetFavouriteDelta(Cid previousDeltaDfsHash, out FavouriteDeltaBroadcast favourite) @@ -164,11 +165,11 @@ public bool TryGetFavouriteDelta(Cid previousDeltaDfsHash, out FavouriteDeltaBro favourite = new FavouriteDeltaBroadcast { Candidate = bestCandidate, - VoterId = _localPeerIdentifier + Voter = _localPeerIdentifier.GetKvmAddressByteString() }; _logger.Debug("Retrieved favourite candidate delta {candidate} for the successor of delta {previousDelta}", - CidHelper.Cast(bestCandidate.Hash.ToByteArray()), + bestCandidate.Hash.ToByteArray().ToCid(), previousDeltaDfsHash); return true; @@ -177,20 +178,22 @@ public bool TryGetFavouriteDelta(Cid previousDeltaDfsHash, out FavouriteDeltaBro private int GetProducerRankFactor(CandidateDeltaBroadcast candidate) { var preferredProducers = _deltaProducersProvider - .GetDeltaProducersFromPreviousDelta(CidHelper.Cast(candidate.PreviousDeltaDfsHash.ToByteArray())); + .GetDeltaProducersFromPreviousDelta(candidate.PreviousDeltaDfsHash.ToByteArray().ToCid()); + + Address address = new(candidate.Producer.ToByteArray()); var ranking = preferredProducers.ToList() - .FindIndex(p => p.Equals(candidate.ProducerId)); + .FindIndex(p => p.Equals(address)); - var identifier = candidate.ProducerId; + var identifier = candidate.Producer; _logger.Verbose("ranking for block produced by {producerId} = {ranking}", identifier, ranking); if (ranking == -1) { throw new KeyNotFoundException( - $"Producer {candidate.ProducerId} " + + $"Producer {candidate.Producer} " + "should not be sending candidate deltas with previous hash " + - $"{CidHelper.Cast(candidate.PreviousDeltaDfsHash.ToByteArray())}"); + $"{candidate.PreviousDeltaDfsHash.ToByteArray().ToCid()} {candidate.PreviousDeltaDfsHash.ToByteArray().ToCid().Hash.ToBase32()}"); } return preferredProducers.Count - ranking; diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/FavouriteByHashAndVoterComparer.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/FavouriteByHashAndVoterComparer.cs index da838d45da..8f752d2d89 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/FavouriteByHashAndVoterComparer.cs +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/FavouriteByHashAndVoterComparer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,6 +22,7 @@ #endregion using System.Collections.Generic; +using System.Text; using Catalyst.Core.Lib.Util; using Catalyst.Protocol.Wire; using Google.Protobuf; @@ -56,16 +57,16 @@ public int Compare(FavouriteDeltaBroadcast x, FavouriteDeltaBroadcast y) return candidateHashComparison; } - return ByteUtil.ByteListComparer.Default.Compare( - x.VoterId?.ToByteArray(), - y.VoterId?.ToByteArray()); + var voterIdXBytes = x.Voter.ToByteArray(); + var voterIdYBytes = y.Voter.ToByteArray(); + return ByteUtil.ByteListComparer.Default.Compare(voterIdXBytes, voterIdYBytes); } private static int CompareCandidateHash(CandidateDeltaBroadcast x, CandidateDeltaBroadcast y) { var xByteArray = x?.Hash?.ToByteArray(); var yByteArray = y?.Hash?.ToByteArray(); - return + return ByteUtil.ByteListMinSizeComparer.Default.Compare( xByteArray, yByteArray); @@ -88,8 +89,8 @@ public int GetHashCode(FavouriteDeltaBroadcast favourite) unchecked { var candidateHash = favourite.Candidate?.Hash == null ? 0 : favourite.Candidate.Hash.GetHashCode(); - var voterIdHash = favourite.VoterId == null ? 0 : favourite.VoterId.GetHashCode(); - return (candidateHash * 397) ^ voterIdHash; + var voterHash = favourite.Voter == null ? 0 : favourite.Voter.GetHashCode(); + return (candidateHash * 397) ^ voterHash; } } } diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/IDeltaProducersProvider.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/IDeltaProducersProvider.cs index a36865da80..9a22cc8a94 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/IDeltaProducersProvider.cs +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/IDeltaProducersProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,14 +22,14 @@ #endregion using System.Collections.Generic; -using Catalyst.Core.Lib.P2P.Repository; -using Catalyst.Protocol.Peer; -using LibP2P; +using Catalyst.Abstractions.P2P.Repository; +using Lib.P2P; +using Nethermind.Core; namespace Catalyst.Core.Modules.Consensus.Deltas { /// - /// This is the service in charge of providing the list of PeerIdentifiers that are eligible for the + /// This is the repository in charge of providing the list of PeerIdentifiers that are eligible for the /// production of the next delta. /// public interface IDeltaProducersProvider @@ -42,7 +42,7 @@ public interface IDeltaProducersProvider /// The list of peers which are eligible for the production of the delta following /// /// - IList GetDeltaProducersFromPreviousDelta(Cid previousDeltaHash); + IList
GetDeltaProducersFromPreviousDelta(Cid previousDeltaHash); /// /// A peer repository containing peers eligible for the production of the next delta. diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/ScoredCandidateDelta.cs b/src/Catalyst.Core.Modules.Consensus/Deltas/ScoredCandidateDelta.cs index b9ac776596..68805116d2 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/ScoredCandidateDelta.cs +++ b/src/Catalyst.Core.Modules.Consensus/Deltas/ScoredCandidateDelta.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Consensus/IO/Observers/CandidateDeltaObserver.cs b/src/Catalyst.Core.Modules.Consensus/IO/Observers/CandidateDeltaObserver.cs index 7e972669e7..1e6bca0c01 100644 --- a/src/Catalyst.Core.Modules.Consensus/IO/Observers/CandidateDeltaObserver.cs +++ b/src/Catalyst.Core.Modules.Consensus/IO/Observers/CandidateDeltaObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,60 +22,84 @@ #endregion using System; +using System.Linq; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Hashing; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Protocol.Wire; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Consensus.IO.Observers { - public class CandidateDeltaObserver : BroadcastObserverBase, IP2PMessageObserver + /** + * Receives candidate delta broadcasts from producers in validation pool. + * Validates hash and then adds to IDeltaVoter + */ + public sealed class CandidateDeltaObserver : BroadcastObserverBase, IP2PMessageObserver { private readonly IDeltaVoter _deltaVoter; private readonly IHashProvider _hashProvider; + private readonly IPeerRepository _peerRepository; - public CandidateDeltaObserver(IDeltaVoter deltaVoter, IHashProvider provider, ILogger logger) + public CandidateDeltaObserver(IDeltaVoter deltaVoter, IPeerRepository peerRepository, IHashProvider provider, ILogger logger) : base(logger) { _deltaVoter = deltaVoter; + _peerRepository = peerRepository; _hashProvider = provider; } - public override void HandleBroadcast(IObserverDto messageDto) + public override void HandleBroadcast(ProtocolMessage message) { try { - Logger.Verbose("received {message} from {port}", messageDto.Payload.CorrelationId.ToCorrelationId(), - messageDto.Payload.PeerId.Port); - var deserialized = messageDto.Payload.FromProtocolMessage(); + MultiAddress multiAddress = new(message.Address); + Logger.Verbose("received {message} from {port}", message.CorrelationId.ToCorrelationId(), + multiAddress.GetPort()); + + // @TODO here we use the protobuff message to parse rather than using the CandidateDeltaBroadcastDao + ///////////////////////////////////////////////////////////////////////////////////////////////// + var deserialized = message.FromProtocolMessage(); + var previousDeltaDfsHashCid = deserialized.PreviousDeltaDfsHash.ToByteArray().ToCid(); + ///////////////////////////////////////////////////////////////////////////////////////////////// - var previousDeltaDfsHashCid = CidHelper.Cast(deserialized.PreviousDeltaDfsHash.ToByteArray()); if (!_hashProvider.IsValidHash(previousDeltaDfsHashCid.Hash.ToArray())) { Logger.Error("PreviousDeltaDfsHash is not a valid hash"); return; } - var hashCid = CidHelper.Cast(deserialized.Hash.ToByteArray()); + ///////////////////////////////////////////////////////////////////////////////////////////////// + var hashCid = deserialized.Hash.ToByteArray().ToCid(); + ///////////////////////////////////////////////////////////////////////////////////////////////// + if (!_hashProvider.IsValidHash(hashCid.Hash.ToArray())) { Logger.Error("Hash is not a valid hash"); return; } - deserialized.IsValid(); + var messagePoaNode = _peerRepository.GetPoaPeersByPublicKey(multiAddress.GetPublicKey()).FirstOrDefault(); + if (messagePoaNode == null) + { + Logger.Error($"Message from IP address '{multiAddress.GetIpAddress()}' with public key '{multiAddress.GetPublicKey()}' is not found in producer node list."); + return; + } - _deltaVoter.OnNext(deserialized); + if (deserialized.IsValid()) + { + _deltaVoter.OnNext(deserialized); + } } catch (Exception exception) { Logger.Error(exception, - $"Failed to process candidate delta broadcast {messageDto.Payload.ToJsonString()}."); + $"Failed to process candidate delta broadcast {message.ToJsonString()}."); } } } diff --git a/src/Catalyst.Core.Modules.Consensus/IO/Observers/DeltaDfsHashObserver.cs b/src/Catalyst.Core.Modules.Consensus/IO/Observers/DeltaDfsHashObserver.cs index 37b26256ef..b696c457dd 100644 --- a/src/Catalyst.Core.Modules.Consensus/IO/Observers/DeltaDfsHashObserver.cs +++ b/src/Catalyst.Core.Modules.Consensus/IO/Observers/DeltaDfsHashObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,13 +22,18 @@ #endregion using System; +using System.Linq; using Catalyst.Abstractions.Consensus.Deltas; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Core.Abstractions.Sync; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Protocol.Wire; +using Dawn; +using MultiFormats; +using Nethermind.Core; using Serilog; namespace Catalyst.Core.Modules.Consensus.IO.Observers @@ -36,33 +41,66 @@ namespace Catalyst.Core.Modules.Consensus.IO.Observers public class DeltaDfsHashObserver : BroadcastObserverBase, IP2PMessageObserver { private readonly IDeltaHashProvider _deltaHashProvider; + private readonly IDeltaElector _deltaElector; + private readonly SyncState _syncState; + private readonly IPeerRepository _peerRepository; - public DeltaDfsHashObserver(IDeltaHashProvider deltaHashProvider, ILogger logger) + public DeltaDfsHashObserver(IDeltaHashProvider deltaHashProvider, + IDeltaElector deltaElector, + SyncState syncState, + IPeerRepository peerRepository, + ILogger logger) : base(logger) { + Guard.Argument(peerRepository, nameof(peerRepository)).NotNull(); + + _syncState = syncState; _deltaHashProvider = deltaHashProvider; + _deltaElector = deltaElector; + _peerRepository = peerRepository; } - public override void HandleBroadcast(IObserverDto messageDto) + public override void HandleBroadcast(ProtocolMessage message) { - try + if (!_syncState.IsSynchronized) { - var deserialised = messageDto.Payload.FromProtocolMessage(); + return; + } - var previousHash = CidHelper.Cast(deserialised.PreviousDeltaDfsHash.ToByteArray()); + try + { + var deserialised = message.FromProtocolMessage(); + var previousHash = deserialised.PreviousDeltaDfsHash.ToByteArray().ToCid(); if (previousHash == null) { Logger.Error("PreviousDeltaDfsHash is not a valid hash"); return; } - var newHash = CidHelper.Cast(deserialised.DeltaDfsHash.ToByteArray()); + var newHash = deserialised.DeltaDfsHash.ToByteArray().ToCid(); if (newHash == null) { Logger.Error("DeltaDfsHash is not a valid hash"); return; } + MultiAddress multiAddress = new(message.Address); + var messagePoaNode = _peerRepository.GetPoaPeersByPublicKey(multiAddress.GetPublicKey()).FirstOrDefault(); + if (messagePoaNode == null) + { + Logger.Error($"Message from IP address '{multiAddress.GetIpAddress()}' with public key '{multiAddress.GetPublicKey()}' is not found in producer node list."); + return; + } + + //Add functionality to check Candidate MerkleRoot against transactions in delta + var mostPopularDelta = _deltaElector.GetMostPopularCandidateDelta(previousHash); + var mostPopularDeltaProducer = new Address(mostPopularDelta.Producer.ToByteArray()); + if (mostPopularDelta == null || multiAddress.GetKvmAddress() != mostPopularDeltaProducer) + { + Logger.Error("Delta is null or not the most popular."); + return; + } + _deltaHashProvider.TryUpdateLatestHash(previousHash, newHash); } catch (Exception exception) diff --git a/src/Catalyst.Core.Modules.Consensus/IO/Observers/FavouriteDeltaObserver.cs b/src/Catalyst.Core.Modules.Consensus/IO/Observers/FavouriteDeltaObserver.cs index 9edf930f40..dbf2de9fff 100644 --- a/src/Catalyst.Core.Modules.Consensus/IO/Observers/FavouriteDeltaObserver.cs +++ b/src/Catalyst.Core.Modules.Consensus/IO/Observers/FavouriteDeltaObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,14 +22,16 @@ #endregion using System; +using System.Linq; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Hashing; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Protocol.Wire; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Consensus.IO.Observers @@ -38,34 +40,44 @@ public class FavouriteDeltaObserver : BroadcastObserverBase messageDto) + public override void HandleBroadcast(ProtocolMessage message) { try { - var deserialized = messageDto.Payload.FromProtocolMessage(); + var deserialized = message.FromProtocolMessage(); - var previousDeltaDfsHashCid = CidHelper.Cast(deserialized.Candidate.PreviousDeltaDfsHash.ToByteArray()); + var previousDeltaDfsHashCid = deserialized.Candidate.PreviousDeltaDfsHash.ToByteArray().ToCid(); if (!_hashProvider.IsValidHash(previousDeltaDfsHashCid.Hash.ToArray())) { Logger.Error("PreviousDeltaDfsHash is not a valid hash"); return; } - var hashCid = CidHelper.Cast(deserialized.Candidate.Hash.ToByteArray()); + var hashCid = deserialized.Candidate.Hash.ToByteArray().ToCid(); if (!_hashProvider.IsValidHash(hashCid.Hash.ToArray())) { Logger.Error("Hash is not a valid hash"); return; } + MultiAddress multiAddress = new(message.Address); + var messagePoaNode = _peerRepository.GetPoaPeersByPublicKey(multiAddress.GetPublicKey()).FirstOrDefault(); + if (messagePoaNode == null) + { + Logger.Error($"Message from IP address '{multiAddress.GetIpAddress()}' with public key '{multiAddress.GetPublicKey()}' is not found in producer node list."); + return; + } + deserialized.IsValid(); _deltaElector.OnNext(deserialized); @@ -73,7 +85,7 @@ public override void HandleBroadcast(IObserverDto messageDto) catch (Exception exception) { Logger.Error(exception, - $"Failed to process favourite delta broadcast {messageDto.Payload.ToJsonString()}."); + $"Failed to process favourite delta broadcast {message.ToJsonString()}."); } } } diff --git a/src/Catalyst.Core.Modules.Consensus/ByFeeTimestampAndHash.cs b/src/Catalyst.Core.Modules.Consensus/TransactionComparerByPriceAndHash.cs similarity index 68% rename from src/Catalyst.Core.Modules.Consensus/ByFeeTimestampAndHash.cs rename to src/Catalyst.Core.Modules.Consensus/TransactionComparerByPriceAndHash.cs index 6357aaaaad..3fe0e04245 100644 --- a/src/Catalyst.Core.Modules.Consensus/ByFeeTimestampAndHash.cs +++ b/src/Catalyst.Core.Modules.Consensus/TransactionComparerByPriceAndHash.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,17 +22,17 @@ #endregion using Catalyst.Abstractions.Consensus; -using Catalyst.Core.Lib.Extensions.Protocol.Wire; +using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Util; -using Catalyst.Protocol.Wire; +using Catalyst.Protocol.Transaction; using Google.Protobuf; namespace Catalyst.Core.Modules.Consensus { /// - public class TransactionComparerByFeeTimestampAndHash : ITransactionComparer + public class TransactionComparerByPriceAndHash : ITransactionComparer { - public int Compare(TransactionBroadcast x, TransactionBroadcast y) + public int Compare(PublicEntry x, PublicEntry y) { if (ReferenceEquals(x, y)) { @@ -49,21 +49,16 @@ public int Compare(TransactionBroadcast x, TransactionBroadcast y) return -1; } - var feeComparison = x.SummedEntryFees().CompareTo(y.SummedEntryFees()); - if (feeComparison != 0) - { - return feeComparison; - } + var gasComparison = x.GasPrice.ToUInt256().CompareTo(y.GasPrice.ToUInt256()); - var timeStampComparison = y.Timestamp.CompareTo(x.Timestamp); - if (timeStampComparison != 0) + if (gasComparison != 0) { - return timeStampComparison; + return gasComparison; } return ByteUtil.ByteListMinSizeComparer.Default.Compare(x.Signature.ToByteArray(), y.Signature.ToByteArray()); } - public static ITransactionComparer Default { get; } = new TransactionComparerByFeeTimestampAndHash(); + public static ITransactionComparer Default { get; } = new TransactionComparerByPriceAndHash(); } } diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests.csproj b/src/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests.csproj index 8b0de85b5f..8a02716e7c 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests.csproj +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests.csproj @@ -1,13 +1,23 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Cryptography.BulletProofs.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Cryptography.BulletProofs.Tests.snk true + + 1701;1702;VSTHRD200;CS8002 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests/UnitTests/CryptoWrapperTests.cs b/src/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests/UnitTests/CryptoWrapperTests.cs index 77fc25a9b9..5df5649c2f 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests/UnitTests/CryptoWrapperTests.cs +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs.Tests/UnitTests/CryptoWrapperTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,12 +22,18 @@ #endregion using System; +using System.Collections.Generic; using System.Text; using Catalyst.Abstractions.Cryptography; using Catalyst.Core.Modules.Cryptography.BulletProofs.Exceptions; +using Catalyst.Protocol.Cryptography; +using Catalyst.Protocol.Network; +using Catalyst.Protocol.Transaction; using FluentAssertions; +using Google.Protobuf; using Nethereum.Hex.HexConvertors.Extensions; -using Xunit; +using NUnit.Framework; + namespace Catalyst.Core.Modules.Cryptography.BulletProofs.Tests.UnitTests { @@ -38,196 +44,215 @@ public sealed class CryptoWrapperTests private readonly ICryptoContext _wrapper; private static readonly Random Random = new Random(); - [Fact] + [Test] public void TestGenerateKey() { - IPrivateKey privateKey = _wrapper.GeneratePrivateKey(); + var privateKey = _wrapper.GeneratePrivateKey(); privateKey.Bytes.Length.Should().Be(_wrapper.PrivateKeyLength); } - [Fact] + [Test] public void TestGenerateDifferentKey() { - IPrivateKey privateKey1 = _wrapper.GeneratePrivateKey(); - IPrivateKey privateKey2 = _wrapper.GeneratePrivateKey(); + var privateKey1 = _wrapper.GeneratePrivateKey(); + var privateKey2 = _wrapper.GeneratePrivateKey(); privateKey1.Bytes.Should().NotEqual(privateKey2.Bytes); } - [Fact] + [Test] public void TestGetPublicKeyFromPrivate() { - IPrivateKey privateKey = _wrapper.GeneratePrivateKey(); - IPublicKey publicKey = _wrapper.GetPublicKeyFromPrivateKey(privateKey); + var privateKey = _wrapper.GeneratePrivateKey(); + var publicKey = _wrapper.GetPublicKeyFromPrivateKey(privateKey); publicKey.Bytes.Length.Should().Be(_wrapper.PublicKeyLength); } - [Fact] + [Test] public void TestStdSignVerify() { - IPrivateKey privateKey = _wrapper.GeneratePrivateKey(); - byte[] message = Encoding.UTF8.GetBytes("fa la la la"); - byte[] context = Encoding.UTF8.GetBytes("context"); - ISignature signature = _wrapper.Sign(privateKey, message, context); - bool isVerified = _wrapper.Verify(signature, message, context); + var privateKey = _wrapper.GeneratePrivateKey(); + var message = Encoding.UTF8.GetBytes("fa la la la"); + var context = Encoding.UTF8.GetBytes("context"); + var signature = _wrapper.Sign(privateKey, message, context); + var isVerified = _wrapper.Verify(signature, message, context); isVerified.Should().BeTrue(); } - [Fact] + [Test] public void TestLogicalFailureStdSignVerify() { - IPrivateKey privateKey = _wrapper.GeneratePrivateKey(); - byte[] message1 = Encoding.UTF8.GetBytes("fa la la la"); - byte[] message2 = Encoding.UTF8.GetBytes("fa la la lahhhhhhh"); - byte[] context = Encoding.UTF8.GetBytes("any old context"); - ISignature signature = _wrapper.Sign(privateKey, message1, context); - bool isVerified = _wrapper.Verify(signature, message2, context); + var privateKey = _wrapper.GeneratePrivateKey(); + var message1 = Encoding.UTF8.GetBytes("fa la la la"); + var message2 = Encoding.UTF8.GetBytes("fa la la lahhhhhhh"); + var context = Encoding.UTF8.GetBytes("any old context"); + var signature = _wrapper.Sign(privateKey, message1, context); + var isVerified = _wrapper.Verify(signature, message2, context); isVerified.Should().BeFalse(); } + [Test] + public void TestSigningForMessagesMethodEquivalence() + { + var privateKey = _wrapper.GeneratePrivateKey(); + + var message1 = new PublicEntry {Nonce = 123}; + var message2 = new PublicEntry {Nonce = 34534908}; + + var expected = _wrapper.Sign(privateKey, message1.ToByteArray(), message2.ToByteArray()); + var actual = _wrapper.Sign(privateKey, message1, message2); + + actual.SignatureBytes.Should().BeEquivalentTo(expected.SignatureBytes); + actual.PublicKeyBytes.Should().BeEquivalentTo(expected.PublicKeyBytes); + } + //From https://tools.ietf.org/html/rfc8032#section-7.3 [Theory] - [InlineData("616263", "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "", true)] - [InlineData("616263", "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "a", false)] - [InlineData("616261", "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "", false)] - [InlineData("616263", "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", "0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772", "", false)] - [InlineData("616263", "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083405", "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "", false)] - public void Ed25519PhTestVectors(string message, string signature, string publicKey, string context, bool expectedResult) + [TestCase("616263", + "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", + "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "", true)] + [TestCase("616263", + "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", + "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "a", false)] + [TestCase("616261", + "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", + "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "", false)] + [TestCase("616263", + "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083406", + "0f1d1274943b91415889152e893d80e93275a1fc0b65fd71b4b0dda10ad7d772", "", false)] + [TestCase("616263", + "98a70222f0b8121aa9d30f813d683f809e462b469c7ff87639499bb94e6dae4131f85042463c2a355a2003d062adf5aaa10b8c61e636062aaad11c2a26083405", + "ec172b93ad5e563bf4932c70e1245034c35467ef2efd4d64ebf819683467e2bf", "", false)] + public void Ed25519PhTestVectors(string message, + string signature, + string publicKey, + string context, + bool expectedResult) { var signatureAndMessage = signature.HexToByteArray(); - byte[] signatureBytes = new ArraySegment(signatureAndMessage, 0, _wrapper.SignatureLength).ToArray(); - byte[] publicKeyBytes = publicKey.HexToByteArray(); - byte[] messageBytes = message.HexToByteArray(); - byte[] contextBytes = context.HexToByteArray(); - + var signatureBytes = new ArraySegment(signatureAndMessage, 0, _wrapper.SignatureLength).ToArray(); + var publicKeyBytes = publicKey.HexToByteArray(); + var messageBytes = message.HexToByteArray(); + var contextBytes = context.HexToByteArray(); + var sig = _wrapper.GetSignatureFromBytes(signatureBytes, publicKeyBytes); - + var isVerified = _wrapper.Verify(sig, messageBytes, contextBytes); isVerified.Should().Be(expectedResult); } [Theory] - [InlineData("mL9Z+e5gIfEdfhDWUxkUox886YuiZnhEj3om5AXmWVXJK7dl7/ESkjhbkJsrbzIbuWm8EPSjJ2YicTIcXvfzIA==", "fa la la la")] + [TestCase("mL9Z+e5gIfEdfhDWUxkUox886YuiZnhEj3om5AXmWVXJK7dl7/ESkjhbkJsrbzIbuWm8EPSjJ2YicTIcXvfzIA==", + "fa la la la")] public void TestFailureInvalidSignatureFormat(string sig, string msg) { var privateKey = _wrapper.GeneratePrivateKey(); var publicKeyBytes = _wrapper.GetPublicKeyFromPrivateKey(privateKey).Bytes; var signatureBytes = Convert.FromBase64String(sig); var invalidSig = _wrapper.GetSignatureFromBytes(signatureBytes, publicKeyBytes); - - Action action = () => - { - _wrapper.Verify(invalidSig, Encoding.UTF8.GetBytes(msg), "".HexToByteArray()); - }; - + + Action action = () => { _wrapper.Verify(invalidSig, Encoding.UTF8.GetBytes(msg), "".HexToByteArray()); }; + action.Should().Throw(); } - [Fact] - public void Is_PrivateKey_Length_Positive() + [Test] + public void TestVerifyingForMessagesMethodEquivalence() { - _wrapper.PrivateKeyLength.Should().BePositive(); - } + var privateKey = _wrapper.GeneratePrivateKey(); - [Fact] - public void Is_PublicKey_Length_Positive() - { - _wrapper.PublicKeyLength.Should().BePositive(); - } + var message1 = new PublicEntry {Nonce = 123}; + var context = new SigningContext {NetworkType = NetworkType.Mainnet}; - [Fact] - public void Is_Signature_Length_Positive() - { - _wrapper.SignatureLength.Should().BePositive(); + var signature = _wrapper.Sign(privateKey, message1.ToByteArray(), context.ToByteArray()); + + var expected = _wrapper.Verify(signature, message1.ToByteArray(), context.ToByteArray()); + var actual = _wrapper.Verify(signature, message1, context); + + actual.Should().Be(expected); } - [Fact] + [Test] + public void Is_PrivateKey_Length_Positive() { _wrapper.PrivateKeyLength.Should().BePositive(); } + + [Test] + public void Is_PublicKey_Length_Positive() { _wrapper.PublicKeyLength.Should().BePositive(); } + + [Test] + public void Is_Signature_Length_Positive() { _wrapper.SignatureLength.Should().BePositive(); } + + [Test] public void Is_Signature_Context_Max_Length_Positive() { _wrapper.SignatureContextMaxLength.Should().BePositive(); } - [Fact] + [Test] public void PublicKey_Can_Be_Created_With_Valid_Input() { const string publicKeyHex = "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908025"; - byte[] publicKeyBytes = publicKeyHex.HexToByteArray(); - IPublicKey publicKey = _wrapper.GetPublicKeyFromBytes(publicKeyBytes); + var publicKeyBytes = publicKeyHex.HexToByteArray(); + var publicKey = _wrapper.GetPublicKeyFromBytes(publicKeyBytes); publicKey.Should().NotBe(null); } - [Fact] + [Test] public void PublicKeyFromBytes_Throws_SignatureException_On_Invalid_Point() { const string publicKeyHex = "fc51cd8e6218a1a38da47ed00230f0580816ed13ba3303ac5deb911548908024"; - byte[] publicKeyBytes = publicKeyHex.HexToByteArray(); - Action action = () => - { - _wrapper.GetPublicKeyFromBytes(publicKeyBytes); - }; + var publicKeyBytes = publicKeyHex.HexToByteArray(); + Action action = () => { _wrapper.GetPublicKeyFromBytes(publicKeyBytes); }; action.Should().Throw(); } - [Fact] + [Test] public void PublicKeyFromBytes_Throws_ArgumentException_On_Invalid_Length_PublicKey() { var publicKeyBytes = GenerateRandomByteArray(_wrapper.PublicKeyLength - 1); - Action action = () => - { - _wrapper.GetPublicKeyFromBytes(publicKeyBytes); - }; + Action action = () => { _wrapper.GetPublicKeyFromBytes(publicKeyBytes); }; action.Should().Throw(); } - [Fact] + [Test] public void PrivateKey_Can_Be_Created_With_Valid_Input() - { + { var privateKeyBytes = GenerateRandomByteArray(_wrapper.PrivateKeyLength); - IPrivateKey privateKey = _wrapper.GetPrivateKeyFromBytes(privateKeyBytes); + var privateKey = _wrapper.GetPrivateKeyFromBytes(privateKeyBytes); privateKey.Should().NotBe(null); } - [Fact] + [Test] public void PrivateKeyFromBytes_Throws_ArgumentException_On_Invalid_Length_PrivateKey() { var privateKeyBytes = GenerateRandomByteArray(_wrapper.PrivateKeyLength + 1); - Action action = () => - { - _wrapper.GetPrivateKeyFromBytes(privateKeyBytes); - }; + Action action = () => { _wrapper.GetPrivateKeyFromBytes(privateKeyBytes); }; action.Should().Throw(); } - [Fact] + [Test] public void SignatureFromBytes_Throws_ArgumentException_On_Invalid_PublicKey_Length() { var signatureBytes = GenerateRandomByteArray(_wrapper.SignatureLength); var publicKeyBytes = GenerateRandomByteArray(_wrapper.PrivateKeyLength + 1); - - Action action = () => - { - _wrapper.GetSignatureFromBytes(signatureBytes, publicKeyBytes); - }; + + Action action = () => { _wrapper.GetSignatureFromBytes(signatureBytes, publicKeyBytes); }; action.Should().Throw(); } - [Fact] + [Test] public void SignatureFromBytes_Throws_ArgumentException_On_Invalid_Signature_Length() { var signatureBytes = GenerateRandomByteArray(_wrapper.SignatureLength - 1); var privateKey = _wrapper.GeneratePrivateKey(); var publicKey = _wrapper.GetPublicKeyFromPrivateKey(privateKey); - - Action action = () => - { - _wrapper.GetSignatureFromBytes(signatureBytes, publicKey.Bytes); - }; + + Action action = () => { _wrapper.GetSignatureFromBytes(signatureBytes, publicKey.Bytes); }; action.Should().Throw(); } - [Fact] + [Test] public void Can_Create_Signature_With_Valid_Input() { var signatureBytes = GenerateRandomByteArray(_wrapper.SignatureLength); @@ -238,17 +263,120 @@ public void Can_Create_Signature_With_Valid_Input() _wrapper.GetSignatureFromBytes(signatureBytes, publicKey.Bytes).Should().NotBe(null); } - [Fact] + [Test] public void Signature_Should_Contain_Public_Key_Corresponding_To_Private_Key() { - IPrivateKey privateKey = _wrapper.GeneratePrivateKey(); - IPublicKey publicKey = _wrapper.GetPublicKeyFromPrivateKey(privateKey); - byte[] message = Encoding.UTF8.GetBytes("fa la la la"); - byte[] context = Encoding.UTF8.GetBytes("context"); - ISignature signature = _wrapper.Sign(privateKey, message, context); + var privateKey = _wrapper.GeneratePrivateKey(); + var publicKey = _wrapper.GetPublicKeyFromPrivateKey(privateKey); + var message = Encoding.UTF8.GetBytes("fa la la la"); + var context = Encoding.UTF8.GetBytes("context"); + var signature = _wrapper.Sign(privateKey, message, context); signature.PublicKeyBytes.Should().Equal(publicKey.Bytes); } + [Test] + public void Batch_Verification_Passes_For_Valid_Batch() + { + var sigs = new List(); + var context = Encoding.UTF8.GetBytes("context"); + var messages = new List + { + Encoding.UTF8.GetBytes("rat"), + Encoding.UTF8.GetBytes("hat"), + Encoding.UTF8.GetBytes("hut"), + Encoding.UTF8.GetBytes("but"), + Encoding.UTF8.GetBytes("bun"), + Encoding.UTF8.GetBytes("run"), + Encoding.UTF8.GetBytes("ran"), + Encoding.UTF8.GetBytes("can"), + Encoding.UTF8.GetBytes("cat"), + Encoding.UTF8.GetBytes("rat") + }; + messages.ForEach(x => + { + sigs.Add(_wrapper.Sign(_wrapper.GeneratePrivateKey(), x, context)); + }); + + var isVerified = _wrapper.BatchVerify(sigs, messages, context); + isVerified.Should().BeTrue(); + } + + [Test] + public void Batch_Verification_Fails_If_Wrong_Message_In_Batch() + { + var sigs = new List(); + var context = Encoding.UTF8.GetBytes("context"); + var messages = new List + { + Encoding.UTF8.GetBytes("abc"), + Encoding.UTF8.GetBytes("123"), + Encoding.UTF8.GetBytes("xyz"), + }; + messages.ForEach(x => sigs.Add(_wrapper.Sign(_wrapper.GeneratePrivateKey(), Encoding.UTF8.GetBytes("change of message"), context))); + + var isVerified = _wrapper.BatchVerify(sigs, messages, context); + isVerified.Should().BeFalse(); + } + + [Test] + public void Batch_Verification_Fails_If_Wrong_Context_In_Batch() + { + var sigs = new List(); + var context = Encoding.UTF8.GetBytes("context"); + var context2 = Encoding.UTF8.GetBytes("this context is different"); + var messages = new List + { + Encoding.UTF8.GetBytes("abc"), + Encoding.UTF8.GetBytes("123"), + Encoding.UTF8.GetBytes("xyz"), + }; + messages.ForEach(x => sigs.Add(_wrapper.Sign(_wrapper.GeneratePrivateKey(), x, context))); + + var isVerified = _wrapper.BatchVerify(sigs, messages, context2); + isVerified.Should().BeFalse(); + } + + [Test] + public void Batch_Verification_Fails_For_One_Incorrect_Signature_PublicKey_Pair_In_Batch() + { + var sigs = new List(); + var context = Encoding.UTF8.GetBytes("context"); + var messages = new List + { + Encoding.UTF8.GetBytes("abc"), + Encoding.UTF8.GetBytes("123"), + Encoding.UTF8.GetBytes("xyz"), + }; + messages.ForEach(x => sigs.Add(_wrapper.Sign(_wrapper.GeneratePrivateKey(), x, context))); + sigs[1] = _wrapper.GetSignatureFromBytes(sigs[1].SignatureBytes, sigs[2].PublicKeyBytes); + + var isVerified = _wrapper.BatchVerify(sigs, messages, context); + isVerified.Should().BeFalse(); + } + + [Test] + public void Batch_Verification_Passes_For_Batch_Size_N() + { + const int N = 100; + var messages = new List(); + var signatures = new List(); + var context = Encoding.UTF8.GetBytes("context"); + for (var i = 0; i < N; i++) + { + var bytes = new byte[255]; + Random.NextBytes(bytes); + messages.Add(bytes); + } + + messages.ForEach(x => + { + signatures.Add(_wrapper.Sign(_wrapper.GeneratePrivateKey(), x, context)); + }); + + var isVerified = _wrapper.BatchVerify(signatures, messages, context); + isVerified.Should().BeTrue(); + } + private static byte[] GenerateRandomByteArray(int length) { var buf = new byte[length]; diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/BulletProofsModule.cs b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/BulletProofsModule.cs index 3cf629b554..3b56c50b83 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/BulletProofsModule.cs +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/BulletProofsModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Catalyst.Core.Modules.Cryptography.BulletProofs.csproj b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Catalyst.Core.Modules.Cryptography.BulletProofs.csproj index 341c39d70d..0e75e5f2c5 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Catalyst.Core.Modules.Cryptography.BulletProofs.csproj +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Catalyst.Core.Modules.Cryptography.BulletProofs.csproj @@ -1,11 +1,15 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Cryptography.BulletProofs - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Cryptography.BulletProofs.snk true + true + + + 1701;1702;CS8002 @@ -14,72 +18,8 @@ - - - - - - PreserveNewest - - - - - PreserveNewest - - - - - PreserveNewest - - - - - PreserveNewest - - - - - - - - - - PreserveNewest - - - - - - - PreserveNewest - - - - - - - PreserveNewest - - - - - - - - - - - - - - - - - - - - - - + + + diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Error.cs b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Error.cs index 4c233ec827..92800b226a 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Error.cs +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Error.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,6 +25,7 @@ using System.Reflection; using System.Resources; using Catalyst.Core.Modules.Cryptography.BulletProofs.Exceptions; +using Catalyst.Protocol.Cryptography; namespace Catalyst.Core.Modules.Cryptography.BulletProofs { @@ -37,42 +38,39 @@ internal static class Error new ResourceManager(typeof(Error).FullName, typeof(Error).GetTypeInfo().Assembly)); - internal static void ThrowErrorFromErrorCode(string errorCode) + internal static void ThrowErrorFromErrorCode(ErrorCode errorCode) { switch (errorCode) - { - case "101": - ThrowSignatureException(); + { + case ErrorCode.NoError: + break; + case ErrorCode.InvalidSignature: + case ErrorCode.InvalidPublicKey: + case ErrorCode.InvalidPrivateKey: + case ErrorCode.SignatureVerificationFailure: + case ErrorCode.InvalidContextLength: + ThrowSignatureException(errorCode); break; default: - ThrowUnknownException(); + ThrowUnknownException(errorCode); break; } } - private static string ErrorFromErrorNameAdditionalInfo(string name) - { - string error = ErrorFromErrorName(name); - string additionalInfo = NativeBinding.GetLastError(); - return error + " - " + additionalInfo + "."; - } - - private static string ErrorFromErrorName(string name) + private static string ErrorDescFromErrorCode(string name) { return ResourceManager.GetString(name); } - private static void ThrowSignatureException() + private static void ThrowSignatureException(ErrorCode errorCode) { - const string errorName = "Signature_Exception"; - var error = ErrorFromErrorNameAdditionalInfo(errorName); + string error = ErrorDescFromErrorCode(errorCode.ToString()); throw new SignatureException(error); } - private static void ThrowUnknownException() + private static void ThrowUnknownException(ErrorCode errorCode) { - const string errorName = "Unknown_Exception"; - var error = ErrorFromErrorNameAdditionalInfo(errorName); + string error = ErrorDescFromErrorCode(errorCode.ToString()); throw new UnknownException(error); } @@ -96,7 +94,7 @@ internal static void ThrowArgumentExceptionSignatureLength(int requiredLength) private static void ThrowArgumentExceptionLength(string errorName, int requiredLength) { - var error = ErrorFromErrorName(errorName); + var error = ErrorDescFromErrorCode(errorName); throw new ArgumentException(string.Format(error, requiredLength)); } } diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Error.resx b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Error.resx index 328480f9e0..db533d2ea0 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Error.resx +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Error.resx @@ -117,10 +117,22 @@ System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - Unknown error + An unknown error has occurred - - Signature error + + The signature does not correspond to a valid point on the elliptic curve. + + + The public key does not represent a valid point on the elliptic curve. + + + The private key is invalid. + + + The signature could not be validated against the information provided. + + + The context exceeded the maximum allowed length. The length of the public key must be {0}. @@ -131,4 +143,4 @@ The length of the signature must be {0}. - \ No newline at end of file + diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Exceptions/SignatureException.cs b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Exceptions/SignatureException.cs index 5d3cb54da5..7bb98c5f9d 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Exceptions/SignatureException.cs +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Exceptions/SignatureException.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Exceptions/UnknownException.cs b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Exceptions/UnknownException.cs index f8b2a021db..1c015481cd 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Exceptions/UnknownException.cs +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Exceptions/UnknownException.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/FfiWrapper.cs b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/FfiWrapper.cs index 4550f04bbe..1ec8bc851a 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/FfiWrapper.cs +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/FfiWrapper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,6 +21,7 @@ #endregion +using System; using System.Collections.Generic; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.Types; @@ -38,16 +39,21 @@ public sealed class FfiWrapper : ICryptoContext public int SignatureContextMaxLength => NativeBinding.SignatureContextMaxLength; - public ISignature Sign(IPrivateKey privateKey, byte[] messageBytes, byte[] contextBytes) + public ISignature Sign(IPrivateKey privateKey, ReadOnlySpan message, ReadOnlySpan context) { - return NativeBinding.StdSign(privateKey.Bytes, messageBytes, contextBytes); + return NativeBinding.StdSign(privateKey.Bytes, message, context); } - public bool Verify(ISignature signature, byte[] message, byte[] context) + public bool Verify(ISignature signature, ReadOnlySpan message, ReadOnlySpan context) { return NativeBinding.StdVerify(signature.SignatureBytes, signature.PublicKeyBytes, message, context); } - + + public bool BatchVerify(IList signatures, IList messages, ReadOnlySpan context) + { + return NativeBinding.BatchVerify(signatures, messages, context); + } + public IPrivateKey GeneratePrivateKey() { return NativeBinding.GeneratePrivateKey(); @@ -89,37 +95,37 @@ public byte[] ExportPublicKey(IPublicKey publicKey) public Byte64 CTransactionEntry(IPublicKey publicKey, Byte32 value, Byte32 blinding, Byte32 totalFees, int noParticipants) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public ISignature CtSign(List cTransactionEntries, IPrivateKey privateKey, Byte32 blinding) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public bool CtVerify(List cTransactionEntries, ISignature cTSignature) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public Byte32 GeneratePedersenCommitment(Byte32 value, Byte32 blinding) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public byte[] GenerateRangeProof(Byte32 value, Byte32 blinding) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public bool VerifyRangeProof(byte[] rangeproof, Byte32 oldCommitment, Byte32 deltaCommitment) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } public bool BatchVerifyRangeProof(List rangeproofs, List oldCommitments, List deltaCommitments) { - throw new System.NotImplementedException(); + throw new NotImplementedException(); } } } diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/NativeBinding.cs b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/NativeBinding.cs index 0f3540a7aa..b03fb68af4 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/NativeBinding.cs +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/NativeBinding.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,35 +21,43 @@ #endregion +using System; +using System.Collections.Generic; +using System.Linq; +using System.Runtime.CompilerServices; using System.Runtime.InteropServices; using Catalyst.Abstractions.Cryptography; +using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Modules.Cryptography.BulletProofs.Types; +using Catalyst.Protocol.Cryptography; +using Google.Protobuf; +using Signature = Catalyst.Core.Modules.Cryptography.BulletProofs.Types.Signature; namespace Catalyst.Core.Modules.Cryptography.BulletProofs { public static class NativeBinding { - private const string Library = "catalystffi"; + private const string Library = "catalyst_ffi"; [DllImport(Library, CallingConvention = CallingConvention.Cdecl)] - private static extern int generate_key(byte[] bytes); + private static extern int generate_private_key(byte[] bytes); internal static IPrivateKey GeneratePrivateKey() { var key = new byte[PrivateKeyLength]; - var errorCode = generate_key(key); - if (errorCode != 0) + var responseCode = generate_private_key(key); + if (Enum.TryParse(responseCode.ToString(), out var errorCode) && errorCode != ErrorCode.NoError) { - Error.ThrowErrorFromErrorCode(errorCode.ToString()); + Error.ThrowErrorFromErrorCode(errorCode); } return new PrivateKey(key); } [DllImport(Library, CallingConvention = CallingConvention.Cdecl)] - private static extern int std_sign(byte[] signature, byte[] publicKey, byte[] privateKey, byte[] message, int messageLength, byte[] context, int contextLength); + private static extern unsafe int std_sign(byte[] signature, byte[] publicKey, byte[] privateKey, byte* message, int messageLength, byte* context, int contextLength); - internal static ISignature StdSign(byte[] privateKey, byte[] message, byte[] context) + internal static unsafe ISignature StdSign(byte[] privateKey, ReadOnlySpan message, ReadOnlySpan context) { if (privateKey.Length != PrivateKeyLength) { @@ -59,41 +67,64 @@ internal static ISignature StdSign(byte[] privateKey, byte[] message, byte[] con var signature = new byte[SignatureLength]; var publicKey = new byte[PublicKeyLength]; - var errorCode = std_sign(signature, publicKey, privateKey, message, message.Length, context, context.Length); - if (errorCode != 0) + byte b = 0; + var empty = (byte*) Unsafe.AsPointer(ref b); // ffi requires a valid memory location even if buffer is empty, this creates a valid pointer to the stack + + fixed (byte* messageHandle = message) { - Error.ThrowErrorFromErrorCode(errorCode.ToString()); - } + fixed (byte* contextHandle = context) + { + var responseCode = std_sign(signature, publicKey, privateKey, message.Length > 0 ? messageHandle : empty, message.Length, context.Length > 0 ? contextHandle : empty, context.Length); - return new Signature(signature, publicKey); + if (Enum.TryParse(responseCode.ToString(), out var errorCode) && errorCode != ErrorCode.NoError) + { + Error.ThrowErrorFromErrorCode(errorCode); + } + + return new Signature(signature, publicKey); + } + } } - + [DllImport(Library, CallingConvention = CallingConvention.Cdecl)] - private static extern int std_verify(byte[] signature, byte[] publicKey, byte[] message, int messageLength, byte[] context, int contextLength, byte[] b); + private static extern unsafe int std_verify(byte[] signature, byte[] publicKey, byte* message, int messageLength, byte* context, int contextLength); - internal static bool StdVerify(byte[] signature, byte[] publicKey, byte[] message, byte[] context) + internal static unsafe bool StdVerify(byte[] signature, byte[] publicKey, ReadOnlySpan message, ReadOnlySpan context) { if (signature.Length != SignatureLength) - { + { Error.ThrowArgumentExceptionSignatureLength(SignatureLength); - } - + } + if (publicKey.Length != PublicKeyLength) - { + { Error.ThrowArgumentExceptionPublicKeyLength(PublicKeyLength); - } - - var isVerified = new byte[1]; + } - var errorCode = std_verify(signature, publicKey, message, message.Length, context, context.Length, - isVerified); + byte b = 0; + var empty = (byte*) Unsafe.AsPointer(ref b); // ffi requires a valid memory location even if buffer is empty, this creates a valid pointer to the stack - if (errorCode != 0) + fixed (byte* messageHandle = message) { - Error.ThrowErrorFromErrorCode(errorCode.ToString()); + fixed (byte* contextHandle = context) + { + var responseCode = std_verify(signature, publicKey, message.Length > 0 ? messageHandle : empty, message.Length, context.Length > 0 ? contextHandle : empty, context.Length); + + Enum.TryParse(responseCode.ToString(), out var errorCode); + + if (errorCode == ErrorCode.NoError) + { + return true; + } + + if (errorCode != ErrorCode.SignatureVerificationFailure) + { + Error.ThrowErrorFromErrorCode(errorCode); + } + } } - return isVerified[0] == 1; + return false; } [DllImport(Library, CallingConvention = CallingConvention.Cdecl)] @@ -102,15 +133,15 @@ internal static bool StdVerify(byte[] signature, byte[] publicKey, byte[] messag internal static void ValidatePublicKeyOrThrow(byte[] publicKey) { if (publicKey.Length != PublicKeyLength) - { + { Error.ThrowArgumentExceptionPublicKeyLength(PublicKeyLength); - } + } - var errorCode = validate_public_key(publicKey); + var responseCode = validate_public_key(publicKey); - if (errorCode != 0) + if (Enum.TryParse(responseCode.ToString(), out var errorCode) && errorCode != ErrorCode.NoError) { - Error.ThrowErrorFromErrorCode(errorCode.ToString()); + Error.ThrowErrorFromErrorCode(errorCode); } } @@ -126,23 +157,44 @@ internal static IPublicKey GetPublicKeyFromPrivate(byte[] privateKey) var publicKeyBytes = new byte[PublicKeyLength]; publickey_from_private(publicKeyBytes, privateKey); - + return new PublicKey(publicKeyBytes); } - internal static string GetLastError() + [DllImport(Library, CallingConvention = CallingConvention.Cdecl)] + private static extern unsafe int verify_batch(byte* batchInfo, int batchLength); + + internal static unsafe bool BatchVerify(IList signatures, IList messages, ReadOnlySpan context) { - var errorLength = last_error_length(); - if (errorLength <= 0) return ""; - var readInto = new byte[errorLength - 1]; - return last_error_message(readInto, errorLength) > 0 ? System.Text.Encoding.UTF8.GetString(readInto) : ""; - } + SignatureBatch sigBatch = new(); + sigBatch.Signatures.AddRange(signatures.Select(x => x.SignatureBytes.ToByteString())); + sigBatch.PublicKeys.AddRange(signatures.Select(x => x.PublicKeyBytes.ToByteString())); + sigBatch.Messages.AddRange(messages.Select(x => x.ToByteString())); + sigBatch.Context = context.ToArray().ToByteString(); + var sigBatchBytes = sigBatch.ToByteArray(); - [DllImport(Library, CallingConvention = CallingConvention.Cdecl)] - private static extern int last_error_length(); + byte b = 0; + var empty = (byte*) Unsafe.AsPointer(ref b); - [DllImport(Library, CallingConvention = CallingConvention.Cdecl)] - private static extern int last_error_message(byte[] errorBuffer, int messageLength); + fixed (byte* batchHandle = sigBatchBytes) + { + var error_code = verify_batch(sigBatchBytes.Length > 0 ? batchHandle : empty, sigBatchBytes.Length); + ErrorCode.TryParse(error_code.ToString(), out var errorCode); + + if (errorCode == ErrorCode.NoError) + { + return true; + } + + if (errorCode != ErrorCode.BatchVerificationFailure) + { + Error.ThrowErrorFromErrorCode(errorCode); + } + + + return false; + } + } [DllImport(Library, CallingConvention = CallingConvention.Cdecl)] private static extern int get_private_key_length(); @@ -164,7 +216,7 @@ public static int PrivateKeyLength { if (_privateKeyLength == 0) { - _privateKeyLength = get_private_key_length(); + _privateKeyLength = get_private_key_length(); } return _privateKeyLength; @@ -179,7 +231,7 @@ public static int PublicKeyLength { if (_publicKeyLength == 0) { - _publicKeyLength = get_public_key_length(); + _publicKeyLength = get_public_key_length(); } return _publicKeyLength; @@ -194,7 +246,7 @@ public static int SignatureLength { if (_signatureLength == 0) { - _signatureLength = get_signature_length(); + _signatureLength = get_signature_length(); } return _signatureLength; @@ -209,7 +261,7 @@ public static int SignatureContextMaxLength { if (_signatureContextMaxLength == 0) { - _signatureContextMaxLength = get_max_context_length(); + _signatureContextMaxLength = get_max_context_length(); } return _signatureContextMaxLength; diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/PrivateKey.cs b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/PrivateKey.cs index d5a0059dd6..ccf565b07b 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/PrivateKey.cs +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/PrivateKey.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/PublicKey.cs b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/PublicKey.cs index 443f18288b..fe3ac64042 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/PublicKey.cs +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/PublicKey.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/Signature.cs b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/Signature.cs index a027111596..96b35cd87c 100644 --- a/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/Signature.cs +++ b/src/Catalyst.Core.Modules.Cryptography.BulletProofs/Types/Signature.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/Catalyst.Core.Modules.Dfs.Tests.csproj b/src/Catalyst.Core.Modules.Dfs.Tests/Catalyst.Core.Modules.Dfs.Tests.csproj index bcc85e2e3d..d44d25b8df 100644 --- a/src/Catalyst.Core.Modules.Dfs.Tests/Catalyst.Core.Modules.Dfs.Tests.csproj +++ b/src/Catalyst.Core.Modules.Dfs.Tests/Catalyst.Core.Modules.Dfs.Tests.csproj @@ -1,22 +1,42 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Dfs.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Dfs.Tests.snk true - + + 1701;1702;VSTHRD200;CS8002 + + + + + + + + PreserveNewest + + + PreserveNewest + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + - - - - diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/BlockExchange/BitSwapTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/BlockExchange/BitSwapTest.cs new file mode 100644 index 0000000000..e3d1e6a424 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/BlockExchange/BitSwapTest.cs @@ -0,0 +1,167 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Text; +using System.Threading; +using Catalyst.Core.Lib.Dag; +using Catalyst.Core.Modules.Dfs.BlockExchange; +using Catalyst.Core.Modules.Hashing; +using Catalyst.TestUtils; +using FluentAssertions; +using Lib.P2P; +using MultiFormats.Registry; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.BlockExchange +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class BitSwapTest + { + private readonly Peer _self = new Peer + { + Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCC5r4nQBtnd9qgjnG8fBN5+gnqIeWEIcUFUdCG4su/vrbQ1py8XGKNUBuDjkyTv25Gd3hlrtNJV3eOKZVSL8ePAgMBAAE=" + }; + + public BitSwapTest() + { + new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + } + + [Test] + public void WantList() + { + var bitSwapService = new BitSwapService(new SwarmService(_self)); + Assert.AreEqual(0, bitSwapService.PeerWants(_self.Id).Count()); + + var cid = new DagNode(Encoding.UTF8.GetBytes("BitswapTest unknown block")).Id; + var cancel = new CancellationTokenSource(); + var _ = bitSwapService.WantAsync(cid, _self.Id, cancel.Token); + bitSwapService.PeerWants(_self.Id).ToArray().Should().Contain(cid); + + bitSwapService.Unwant(cid); + bitSwapService.PeerWants(_self.Id).ToArray().Should().NotContain(cid); + } + + [Test] + public void Want_Cancel() + { + var bitSwapService = new BitSwapService(new SwarmService(_self)); + var cid = new DagNode(Encoding.UTF8.GetBytes("BitswapTest unknown block")).Id; + var cancel = new CancellationTokenSource(); + var task = bitSwapService.WantAsync(cid, _self.Id, cancel.Token); + bitSwapService.PeerWants(_self.Id).ToArray().Should().Contain(cid); + + cancel.Cancel(); + Assert.True(task.IsCanceled); + + bitSwapService.PeerWants(_self.Id).ToArray().Should().NotContain(cid); + } + + [Test] + public void Block_Needed() + { + var bitSwapService = new BitSwapService(new SwarmService(_self)); + var cid1 = new DagNode(Encoding.UTF8.GetBytes("BitswapTest unknown block y")).Id; + var cid2 = new DagNode(Encoding.UTF8.GetBytes("BitswapTest unknown block z")).Id; + var cancel = new CancellationTokenSource(); + var callCount = 0; + + bitSwapService.BlockNeeded += (s, e) => + { + Assert.True(cid1 == e.Id || cid2 == e.Id); + ++callCount; + }; + try + { + bitSwapService.WantAsync(cid1, _self.Id, cancel.Token); + bitSwapService.WantAsync(cid1, _self.Id, cancel.Token); + bitSwapService.WantAsync(cid2, _self.Id, cancel.Token); + bitSwapService.WantAsync(cid2, _self.Id, cancel.Token); + Assert.AreEqual(2, callCount); + } + finally + { + cancel.Cancel(); + } + } + + [Test] + public void WantUnwantTests() + { + var bitSwapService = new BitSwapService(new SwarmService(_self)); + var cid = new DagNode(Encoding.UTF8.GetBytes("BitswapTest unknown block")).Id; + var cancel = new CancellationTokenSource(); + var task = bitSwapService.WantAsync(cid, _self.Id, cancel.Token); + + bitSwapService.PeerWants(_self.Id).ToArray().Should().Contain(cid); + + bitSwapService.Unwant(cid); + Assert.True(task.IsCanceled); + bitSwapService.PeerWants(_self.Id).ToArray().Should().NotContain(cid); + } + + [Test] + public void Found() + { + var bitSwapService = new BitSwapService(new SwarmService(_self)); + Assert.AreEqual(0, bitSwapService.PeerWants(_self.Id).Count()); + + var a = new DagNode(Encoding.UTF8.GetBytes("BitswapTest found block a")); + var b = new DagNode(Encoding.UTF8.GetBytes("BitswapTest found block b")); + var cancel = new CancellationTokenSource(); + var task = bitSwapService.WantAsync(a.Id, _self.Id, cancel.Token); + Assert.False(task.IsCompleted); + bitSwapService.PeerWants(_self.Id).ToArray().Should().Contain(a.Id); + + bitSwapService.Found(b); + Assert.False(task.IsCompleted); + bitSwapService.PeerWants(_self.Id).ToArray().Should().Contain(a.Id); + + bitSwapService.Found(a); + Assert.True(task.IsCompleted); + bitSwapService.PeerWants(_self.Id).ToArray().Should().NotContain(a.Id); + a.DataBytes.Should().Contain(task.Result.DataBytes); + } + + [Test] + public void Found_Count() + { + var bitSwapService = new BitSwapService(new SwarmService(_self)); + + var a = new DagNode(Encoding.UTF8.GetBytes("BitswapTest found block a")); + Assert.AreEqual(0, bitSwapService.Found(a)); + + var cancel = new CancellationTokenSource(); + var task1 = bitSwapService.WantAsync(a.Id, _self.Id, cancel.Token); + var task2 = bitSwapService.WantAsync(a.Id, _self.Id, cancel.Token); + Assert.AreEqual(2, bitSwapService.Found(a)); + + Assert.True(task1.IsCompleted); + Assert.True(task2.IsCompleted); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/Catalyst/CatalystProtocolTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/Catalyst/CatalystProtocolTest.cs new file mode 100644 index 0000000000..0f19890ba9 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/Catalyst/CatalystProtocolTest.cs @@ -0,0 +1,142 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Protocol.Wire; +using Lib.P2P.Protocols; +using NUnit.Framework; +using System.Reactive.Linq; +using System; +using System.Threading; +using Catalyst.TestUtils; + +namespace Lib.P2P.Tests.Protocols +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public class CatalystProtocolTest + { + private readonly Peer self = new Peer + { + AgentVersion = "self", + Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCC5r4nQBtnd9qgjnG8fBN5+gnqIeWEIcUFUdCG4su/vrbQ1py8XGKNUBuDjkyTv25Gd3hlrtNJV3eOKZVSL8ePAgMBAAE=" + }; + + private readonly Peer other = new Peer + { + AgentVersion = "other", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + + [Test] + public async Task Can_Send_ProtocolMessage_Using_PeerId() + { + var autoResetEvent = new AutoResetEvent(false); + + var swarmB = new SwarmService(other); + await swarmB.StartAsync(); + var catalystProtocolB = new CatalystProtocol(swarmB); + await catalystProtocolB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/5001"); + + var swarm = new SwarmService(self); + var catalystProtocolA = new CatalystProtocol(swarm); + await swarm.StartAsync(); + await swarm.StartListeningAsync("/ip4/127.0.0.1/tcp/5002"); + + await catalystProtocolA.StartAsync(); + try + { + await swarm.ConnectAsync(peerBAddress); + + var protocolMessage = new ProtocolMessage + { + Address = self.Addresses.First().ToString() + }; + + await catalystProtocolA.SendAsync(other.Id, protocolMessage); + catalystProtocolB.MessageStream.Subscribe(message => + { + autoResetEvent.Set(); + }); + + autoResetEvent.WaitOne(); + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + await catalystProtocolB.StopAsync(); + await catalystProtocolA.StopAsync(); + } + } + + [Test] + public async Task Can_Send_ProtocolMessage_Using_MultiAddress() + { + var autoResetEvent = new AutoResetEvent(false); + + var swarmB = new SwarmService(other); + await swarmB.StartAsync(); + var catalystProtocolB = new CatalystProtocol(swarmB); + await catalystProtocolB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/5003"); + + var swarm = new SwarmService(self); + var catalystProtocolA = new CatalystProtocol(swarm); + await swarm.StartAsync(); + await swarm.StartListeningAsync("/ip4/127.0.0.1/tcp/5004"); + + await catalystProtocolA.StartAsync(); + try + { + await swarm.ConnectAsync(peerBAddress); + + var protocolMessage = new ProtocolMessage + { + Address = self.Addresses.First().ToString() + }; + + await catalystProtocolA.SendAsync(peerBAddress, protocolMessage); + catalystProtocolB.MessageStream.Subscribe(message => + { + autoResetEvent.Set(); + }); + + autoResetEvent.WaitOne(); + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + await catalystProtocolB.StopAsync(); + await catalystProtocolA.StopAsync(); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/AddFileOptionsTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/AddFileOptionsTest.cs new file mode 100644 index 0000000000..b12d975530 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/AddFileOptionsTest.cs @@ -0,0 +1,77 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Options; +using MultiFormats; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + public class AddFileOptionsTests + { + [Test] + public void Defaults() + { + var options = new AddFileOptions(); + + Assert.AreEqual(true, options.Pin); + Assert.AreEqual(256 * 1024, options.ChunkSize); + Assert.AreEqual(MultiHash.DefaultAlgorithmName, options.Hash); + Assert.AreEqual(false, options.OnlyHash); + Assert.AreEqual(false, options.RawLeaves); + Assert.AreEqual(false, options.Trickle); + Assert.AreEqual(false, options.Wrap); + Assert.Null(options.Progress); + Assert.Null(options.ProtectionKey); + } + + [Test] + public void Setting() + { + var options = new AddFileOptions + { + Pin = false, + ChunkSize = 2 * 1024, + Hash = "sha2-512", + OnlyHash = true, + RawLeaves = true, + Progress = new Progress(), + Trickle = true, + Wrap = true, + ProtectionKey = "secret" + }; + + Assert.AreEqual(false, options.Pin); + Assert.AreEqual(2 * 1024, options.ChunkSize); + Assert.AreEqual("sha2-512", options.Hash); + Assert.AreEqual(true, options.OnlyHash); + Assert.AreEqual(true, options.RawLeaves); + Assert.AreEqual(true, options.Trickle); + Assert.AreEqual(true, options.Wrap); + Assert.NotNull(options.Progress); + Assert.AreEqual("secret", options.ProtectionKey); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BitSwapApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BitSwapApiTest.cs new file mode 100644 index 0000000000..5ffc5da2a9 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BitSwapApiTest.cs @@ -0,0 +1,528 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.BlockExchange.Protocols; +using Catalyst.Abstractions.FileSystem; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.Dag; +using Catalyst.Core.Modules.Dfs.BlockExchange.Protocols; +using Catalyst.TestUtils; +using FluentAssertions; +using Lib.P2P; +using MultiFormats; +using NSubstitute; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class BitSwapApiTest + { + private IDfsService _dfsService; + private IDfsService _dfsServiceOther; + + [SetUp] + public void Init() + { + var fileSystem1 = Substitute.For(); + fileSystem1.GetCatalystDataDir().Returns(new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, + $"dfs1-_{DateTime.Now:yyMMddHHmmssffff}"))); + + var fileSystem2 = Substitute.For(); + fileSystem2.GetCatalystDataDir().Returns(new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, + $"dfs2-_{DateTime.Now:yyMMddHHmmssffff}"))); + + _dfsService = TestDfs.GetTestDfs(fileSystem1); + _dfsServiceOther = TestDfs.GetTestDfs(fileSystem2); + } + + /// + /// @TODO this assert is commented out + /// + /// + /// + [Test] + public async Task UnWant() + { + await _dfsService.StartAsync(); + + try + { + var cts = new CancellationTokenSource(); + var block = new DagNode(Encoding.UTF8.GetBytes("BitSwapApiTest unknown block 2")); + var wantTask = _dfsService.BitSwapApi.GetAsync(block.Id, cts.Token).ConfigureAwait(false); + + var endTime = DateTime.Now.AddSeconds(10); + while (true) + { + if (DateTime.Now > endTime) + { + throw new Exception("wanted block is missing"); + } + + await Task.Delay(100, cts.Token); + + var w = await _dfsService.BitSwapApi.WantsAsync(cancel: cts.Token); + if (w.Contains(block.Id)) + { + break; + } + } + + cts.Cancel(); + _dfsService.BitSwapApi.UnWant(block.Id, cts.Token); + var wants = await _dfsService.BitSwapApi.WantsAsync(cancel: cts.Token); + wants.ToArray().Should().NotContain(block.Id); + + //Race condition as wantTask will be on another thread and could create unpredictable behaviour + //Assert.True(wantTask.IsCanceled); + } + finally + { + await _dfsService.StopAsync(); + } + } + + [Test] + public async Task Wants() + { + await _dfsService.StartAsync(); + + try + { + var cts = new CancellationTokenSource(); + var block = new DagNode(Encoding.UTF8.GetBytes("BitSwapApiTest unknown block")); + var wantTask = _dfsService.BitSwapApi.GetAsync(block.Id, cts.Token); + + var endTime = DateTime.Now.AddSeconds(10); + while (true) + { + if (DateTime.Now > endTime) + { + throw new Exception("wanted block is missing"); + } + + await Task.Delay(100, cts.Token); + + var w = await _dfsService.BitSwapApi.WantsAsync(cancel: cts.Token); + if (w.Contains(block.Id)) + { + break; + } + } + + cts.Cancel(); + var wants = await _dfsService.BitSwapApi.WantsAsync(cancel: cts.Token); + wants.ToArray().Should().NotContain(block.Id); + + //Race condition as wantTask will be on another thread and could create unpredictable behaviour + //Assert.True(wantTask.IsCanceled); + } + finally + { + await _dfsService.StopAsync(); + } + } + + [Test] + public async Task OnConnect_Sends_WantList() + { + _dfsService.Options.Discovery.DisableMdns = true; + _dfsService.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + await _dfsService.StartAsync(); + + _dfsServiceOther.Options.Discovery.DisableMdns = true; + _dfsServiceOther.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + await _dfsServiceOther.StartAsync(); + + try + { + var local = _dfsService.LocalPeer; + var remote = _dfsServiceOther.LocalPeer; + TestContext.WriteLine($"this at {local.Addresses.First()}"); + TestContext.WriteLine($"other at {remote.Addresses.First()}"); + + var data = Guid.NewGuid().ToByteArray(); + var cid = new Cid + { + Hash = MultiHash.ComputeHash(data) + }; + + var _ = _dfsService.BlockApi.GetAsync(cid); + await _dfsService.SwarmApi.ConnectAsync(remote.Addresses.First()); + + var endTime = DateTime.Now.AddSeconds(10); + while (DateTime.Now < endTime) + { + var wants = await _dfsServiceOther.BitSwapApi.WantsAsync(local.Id); + if (wants.Contains(cid)) + { + return; + } + + await Task.Delay(200); + } + + throw new Exception("want list not sent"); + } + finally + { + await _dfsServiceOther.StopAsync(); + await _dfsService.StopAsync(); + + _dfsService.Options.Discovery = new DiscoveryOptions(); + _dfsServiceOther.Options.Discovery = new DiscoveryOptions(); + } + } + + [Test] + public async Task GetsBlock_OnConnect() + { + _dfsService.Options.Discovery.DisableMdns = true; + _dfsService.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + await _dfsService.StartAsync(); + + _dfsServiceOther.Options.Discovery.DisableMdns = true; + _dfsServiceOther.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + await _dfsServiceOther.StartAsync(); + + try + { + var data = Guid.NewGuid().ToByteArray(); + var cid = await _dfsServiceOther.BlockApi.PutAsync(data); + + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var getTask = _dfsService.BlockApi.GetAsync(cid, cts.Token); + + var remote = _dfsServiceOther.LocalPeer; + await _dfsService.SwarmApi.ConnectAsync(remote.Addresses.First(), cts.Token); + var block = await getTask; + + Assert.False(getTask.IsCanceled, "task cancelled"); + Assert.False(getTask.IsFaulted, "task faulted"); + Assert.True(getTask.IsCompleted, "task not completed"); + Assert.AreEqual(cid, block.Id); + + data.Should().BeEquivalentTo(block.DataBytes); + + var otherPeer = _dfsServiceOther.LocalPeer; + var ledger = _dfsService.BitSwapApi.GetBitSwapLedger(otherPeer, cts.Token); + + Assert.AreEqual(otherPeer, ledger.Peer); + Assert.AreNotEqual(0UL, ledger.BlocksExchanged); + Assert.AreNotEqual(0UL, ledger.DataReceived); + Assert.AreEqual(0UL, ledger.DataSent); + Assert.True(ledger.IsInDebt); + + // TODO: Timing issue here. ipfsOther could have sent the block + // but not updated the stats yet. +#if false + var localPeer = await ipfs.LocalPeer; + ledger = await ipfsOther.Bitswap.LedgerAsync(localPeer); + Assert.AreEqual(localPeer, ledger.Peer); + Assert.AreNotEqual(0UL, ledger.BlocksExchanged); + Assert.AreEqual(0UL, ledger.DataReceived); + Assert.AreNotEqual(0UL, ledger.DataSent); + Assert.False(ledger.IsInDebt); +#endif + } + finally + { + await _dfsServiceOther.StopAsync(); + await _dfsService.StopAsync(); + + _dfsService.Options.Discovery = new DiscoveryOptions(); + _dfsServiceOther.Options.Discovery = new DiscoveryOptions(); + } + } + + // [Test] + // public async Task GetsBlock_OnConnect_BitSwap1() + // { + // var originalProtocols = (_dfsService.BitSwapService).Protocols; + // var otherOriginalProtocols = (_dfsServiceOther.BitSwapService).Protocols; + // + // (_dfsService.BitSwapService).Protocols = new IBitswapProtocol[] + // { + // new Bitswap1 + // { + // BitswapService = _dfsService.BitSwapService + // } + // }; + // + // _dfsService.Options.Discovery.DisableMdns = true; + // _dfsService.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + // await _dfsService.StartAsync(); + // + // (_dfsServiceOther.BitSwapService).Protocols = new IBitswapProtocol[] + // { + // new Bitswap1 + // { + // BitswapService = _dfsServiceOther.BitSwapService + // } + // }; + // + // _dfsServiceOther.Options.Discovery.DisableMdns = true; + // _dfsServiceOther.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + // await _dfsServiceOther.StartAsync(); + // + // try + // { + // var data = Guid.NewGuid().ToByteArray(); + // var cid = await _dfsServiceOther.BlockApi.PutAsync(data); + // + // var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + // var getTask = _dfsService.BlockApi.GetAsync(cid, cts.Token); + // + // var remote = _dfsServiceOther.LocalPeer; + // await _dfsService.SwarmApi.ConnectAsync(remote.Addresses.First(), cts.Token); + // var block = await getTask; + // + // Assert.False(getTask.IsCanceled, "task cancelled"); + // Assert.False(getTask.IsFaulted, "task faulted"); + // Assert.True(getTask.IsCompleted, "task not completed"); + // Assert.AreEqual(cid, block.Id); + // Assert.AreEqual(data, block.DataBytes); + // + // var otherPeer = _dfsServiceOther.LocalPeer; + // var ledger = await _dfsService.BitSwapApi.LedgerAsync(otherPeer, cts.Token); + // Assert.AreEqual(otherPeer, ledger.Peer); + // Assert.AreNotEqual(0UL, ledger.BlocksExchanged); + // Assert.AreNotEqual(0UL, ledger.DataReceived); + // Assert.AreEqual(0UL, ledger.DataSent); + // Assert.True(ledger.IsInDebt); + // + // // TODO: Timing issue here. ipfsOther could have sent the block + // // but not updated the stats yet. + // #if false + // var localPeer = await ipfs.LocalPeer; + // ledger = await ipfsOther.Bitswap.LedgerAsync(localPeer); + // Assert.AreEqual(localPeer, ledger.Peer); + // Assert.AreNotEqual(0UL, ledger.BlocksExchanged); + // Assert.AreEqual(0UL, ledger.DataReceived); + // Assert.AreNotEqual(0UL, ledger.DataSent); + // Assert.False(ledger.IsInDebt); + // #endif + // } + // finally + // { + // await _dfsServiceOther.StopAsync(); + // await _dfsService.StopAsync(); + // + // _dfsService.Options.Discovery = new DiscoveryOptions(); + // _dfsServiceOther.Options.Discovery = new DiscoveryOptions(); + // + // (_dfsService.BitSwapService).Protocols = originalProtocols; + // (_dfsServiceOther.BitSwapService).Protocols = otherOriginalProtocols; + // } + // } + + [Test] + public async Task GetsBlock_OnConnect_BitSwap11() + { + var originalProtocols = _dfsService.BitSwapService.Protocols; + var otherOriginalProtocols = _dfsServiceOther.BitSwapService.Protocols; + + _dfsService.BitSwapService.Protocols = new IBitswapProtocol[] + { + new Bitswap11 + { + BitswapService = _dfsService.BitSwapService + } + }; + + _dfsService.Options.Discovery.DisableMdns = true; + _dfsService.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + await _dfsService.StartAsync(); + + _dfsServiceOther.BitSwapService.Protocols = new IBitswapProtocol[] + { + new Bitswap11 + { + BitswapService = _dfsServiceOther.BitSwapService + } + }; + _dfsServiceOther.Options.Discovery.DisableMdns = true; + _dfsServiceOther.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + await _dfsServiceOther.StartAsync(); + try + { + var data = Guid.NewGuid().ToByteArray(); + var cid = await _dfsServiceOther.BlockApi.PutAsync(data); + + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); + var getTask = _dfsService.BlockApi.GetAsync(cid, cts.Token); + + var remote = _dfsServiceOther.LocalPeer; + await _dfsService.SwarmApi.ConnectAsync(remote.Addresses.First(), cts.Token); + var block = await getTask; + + Assert.False(getTask.IsCanceled, "task cancelled"); + Assert.False(getTask.IsFaulted, "task faulted"); + Assert.True(getTask.IsCompleted, "task not completed"); + Assert.AreEqual(cid, block.Id); + Assert.AreEqual(data, block.DataBytes); + + var otherPeer = _dfsServiceOther.LocalPeer; + var ledger = _dfsService.BitSwapApi.GetBitSwapLedger(otherPeer, cts.Token); + Assert.AreEqual(otherPeer, ledger.Peer); + Assert.AreNotEqual(0UL, ledger.BlocksExchanged); + Assert.AreNotEqual(0UL, ledger.DataReceived); + Assert.AreEqual(0UL, ledger.DataSent); + Assert.True(ledger.IsInDebt); + + // TODO: Timing issue here. ipfsOther could have sent the block + // but not updated the stats yet. +#if false + var localPeer = await ipfs.LocalPeer; + ledger = await ipfsOther.Bitswap.LedgerAsync(localPeer); + Assert.AreEqual(localPeer, ledger.Peer); + Assert.AreNotEqual(0UL, ledger.BlocksExchanged); + Assert.AreEqual(0UL, ledger.DataReceived); + Assert.AreNotEqual(0UL, ledger.DataSent); + Assert.False(ledger.IsInDebt); +#endif + } + finally + { + await _dfsServiceOther.StopAsync(); + await _dfsService.StopAsync(); + + _dfsService.Options.Discovery = new DiscoveryOptions(); + _dfsServiceOther.Options.Discovery = new DiscoveryOptions(); + + _dfsService.BitSwapService.Protocols = originalProtocols; + _dfsServiceOther.BitSwapService.Protocols = otherOriginalProtocols; + } + } + + [Test] + public async Task GetsBlock_OnRequest() + { + _dfsService.Options.Discovery.DisableMdns = true; + _dfsService.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + await _dfsService.StartAsync(); + + _dfsServiceOther.Options.Discovery.DisableMdns = true; + _dfsServiceOther.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + await _dfsServiceOther.StartAsync(); + + try + { + var cts = new CancellationTokenSource(10000); + var data = Guid.NewGuid().ToByteArray(); + var cid = await _dfsServiceOther.BlockApi.PutAsync(data, cancel: cts.Token); + + var remote = _dfsServiceOther.LocalPeer; + await _dfsService.SwarmApi.ConnectAsync(remote.Addresses.First(), cts.Token); + + var block = await _dfsService.BlockApi.GetAsync(cid, cts.Token); + + Assert.AreEqual(cid, block.Id); + Assert.AreEqual(data, block.DataBytes); + } + finally + { + await _dfsServiceOther.StopAsync(); + await _dfsService.StopAsync(); + _dfsService.Options.Discovery = new DiscoveryOptions(); + _dfsServiceOther.Options.Discovery = new DiscoveryOptions(); + } + } + + [Test] + public async Task GetsBlock_CidV1() + { + await _dfsService.StartAsync(); + await _dfsServiceOther.StartAsync(); + try + { + var data = Guid.NewGuid().ToByteArray(); + var cid = await _dfsServiceOther.BlockApi.PutAsync(data, + "raw"); // @TODO get this from a test prop so we can test against multiple hash algos + + var remote = _dfsServiceOther.LocalPeer; + await _dfsService.SwarmApi.ConnectAsync(remote.Addresses.First()); + + var cts = new CancellationTokenSource(3000); + var block = await _dfsService.BlockApi.GetAsync(cid, cts.Token); + + Assert.AreEqual(cid, block.Id); + Assert.AreEqual(data, block.DataBytes); + } + finally + { + await _dfsServiceOther.StopAsync(); + await _dfsService.StopAsync(); + } + } + + [Test] + public async Task GetBlock_Timeout() + { + var block = new DagNode(Encoding.UTF8.GetBytes("BitSwapApiTest unknown block")); + await _dfsService.StartAsync(); + + try + { + var cts = new CancellationTokenSource(300); + ExceptionAssert.Throws(() => + { + var _ = _dfsService.BitSwapApi.GetAsync(block.Id, cts.Token).Result; + }); + + Assert.AreEqual(0, (await _dfsService.BitSwapApi.WantsAsync(cancel: cts.Token)).Count()); + } + finally + { + await _dfsService.StopAsync(); + } + } + + [Test] + public async Task PeerLedger() + { + await _dfsService.StartAsync(); + + try + { + var peer = _dfsServiceOther.LocalPeer; + var cts = new CancellationTokenSource(300); + var ledger = _dfsService.BitSwapApi.GetBitSwapLedger(peer, cts.Token); + Assert.NotNull(ledger); + } + finally + { + await _dfsService.StopAsync(); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BitswapLedgerTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BitswapLedgerTest.cs new file mode 100644 index 0000000000..f8151cdcf1 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BitswapLedgerTest.cs @@ -0,0 +1,65 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Dfs.CoreApi; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + public class BitswapLedgerTest + { + [Test] + public void Defaults() + { + var ledger = new BitswapLedger(); + Assert.Null(ledger.Peer); + Assert.AreEqual(0ul, ledger.BlocksExchanged); + Assert.AreEqual(0ul, ledger.DataReceived); + Assert.AreEqual(0ul, ledger.DataSent); + Assert.AreEqual(0f, ledger.DebtRatio); + Assert.True(ledger.IsInDebt); + } + + [Test] + public void DebtRatio_Positive() + { + var ledger = new BitswapLedger + { + DataSent = 1024 * 1024 + }; + Assert.True(ledger.DebtRatio >= 1); + Assert.False(ledger.IsInDebt); + } + + [Test] + public void DebtRatio_Negative() + { + var ledger = new BitswapLedger + { + DataReceived = 1024 * 1024 + }; + Assert.True(ledger.DebtRatio < 1); + Assert.True(ledger.IsInDebt); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BlockApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BlockApiTest.cs new file mode 100644 index 0000000000..5cda932f5d --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BlockApiTest.cs @@ -0,0 +1,317 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.TestUtils; +using Lib.P2P; +using MultiFormats; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class BlockApiTest + { + private IDfsService _dfs; + private const string Id = "QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rAQ"; + private readonly byte[] _blob = Encoding.UTF8.GetBytes("blorb"); + + public BlockApiTest() + { + _dfs = TestDfs.GetTestDfs(null, "sha2-256"); + } + + [Test] + public async Task Put_Bytes() + { + var cid = await _dfs.BlockApi.PutAsync(_blob); + Assert.AreEqual(Id, cid.ToString()); + + var data = _dfs.BlockApi.GetAsync(cid).Result; + Assert.AreEqual(_blob.Length, data.Size); + Assert.AreEqual(_blob, data.DataBytes); + } + + [Test] + public void Put_Bytes_TooBig() + { + var data = new byte[_dfs.Options.Block.MaxBlockSize + 1]; + ExceptionAssert.Throws(() => + { + var _ = _dfs.BlockApi.PutAsync(data).Result; + }); + } + + [Test] + public void Put_Bytes_ContentType() + { + var cid = _dfs.BlockApi.PutAsync(_blob, "raw").Result; + Assert.AreEqual("bafkreiaxnnnb7qz2focittuqq3ya25q7rcv3bqynnczfzako47346wosmu", cid.ToString()); + + var data = _dfs.BlockApi.GetAsync(cid).Result; + Assert.AreEqual(_blob.Length, data.Size); + Assert.AreEqual(_blob, data.DataBytes); + } + + [Test] + public void Put_Bytes_Inline_Cid() + { + try + { + _dfs.Options.Block.AllowInlineCid = true; + var cid = _dfs.BlockApi.PutAsync(_blob, "raw").Result; + Assert.True(cid.Hash.IsIdentityHash); + Assert.AreEqual("bafkqablcnrxxeyq", cid.ToString()); + + var data = _dfs.BlockApi.GetAsync(cid).Result; + Assert.AreEqual(_blob.Length, data.Size); + Assert.AreEqual(_blob, data.DataBytes); + + var content = new byte[_dfs.Options.Block.InlineCidLimit]; + cid = _dfs.BlockApi.PutAsync(content, "raw").Result; + Assert.True(cid.Hash.IsIdentityHash); + + content = new byte[_dfs.Options.Block.InlineCidLimit + 1]; + cid = _dfs.BlockApi.PutAsync(content, "raw").Result; + Assert.False(cid.Hash.IsIdentityHash); + } + finally + { + _dfs.Options.Block.AllowInlineCid = false; + } + } + + [Test] + public void Put_Bytes_Hash() + { + var cid = _dfs.BlockApi.PutAsync(_blob, "raw", "sha2-512").Result; + Assert.AreEqual( + "bafkrgqelljziv4qfg5mefz36m2y3h6voaralnw6lwb4f53xcnrf4mlsykkn7vt6eno547tw5ygcz62kxrle45wnbmpbofo5tvu57jvuaf7k7e", + cid.ToString()); + + var data = _dfs.BlockApi.GetAsync(cid).Result; + Assert.AreEqual(_blob.Length, data.Size); + Assert.AreEqual(_blob, data.DataBytes); + } + + [Test] + public void Put_Bytes_Cid_Encoding() + { + var cid = _dfs.BlockApi.PutAsync(_blob, + "raw", + encoding: "base32").Result; + Assert.AreEqual(1, cid.Version); + Assert.AreEqual("base32", cid.Encoding); + + var data = _dfs.BlockApi.GetAsync(cid).Result; + Assert.AreEqual(_blob.Length, data.Size); + Assert.AreEqual(_blob, data.DataBytes); + } + + [Test] + public void Put_Stream() + { + var cid = _dfs.BlockApi.PutAsync(new MemoryStream(_blob)).Result; + Assert.AreEqual(Id, cid.ToString()); + + var data = _dfs.BlockApi.GetAsync(cid).Result; + Assert.AreEqual(_blob.Length, data.Size); + Assert.AreEqual(_blob, data.DataBytes); + } + + [Test] + public void Put_Stream_ContentType() + { + var cid = _dfs.BlockApi.PutAsync(new MemoryStream(_blob), "raw").Result; + Assert.AreEqual("bafkreiaxnnnb7qz2focittuqq3ya25q7rcv3bqynnczfzako47346wosmu", cid.ToString()); + + var data = _dfs.BlockApi.GetAsync(cid).Result; + Assert.AreEqual(_blob.Length, data.Size); + Assert.AreEqual(_blob, data.DataBytes); + } + + [Test] + public void Put_Stream_Hash() + { + var cid = _dfs.BlockApi.PutAsync(new MemoryStream(_blob), "raw", "sha2-512").Result; + Assert.AreEqual( + "bafkrgqelljziv4qfg5mefz36m2y3h6voaralnw6lwb4f53xcnrf4mlsykkn7vt6eno547tw5ygcz62kxrle45wnbmpbofo5tvu57jvuaf7k7e", + cid.ToString()); + + var data = _dfs.BlockApi.GetAsync(cid).Result; + Assert.AreEqual(_blob.Length, data.Size); + Assert.AreEqual(_blob, data.DataBytes); + } + + [Test] + public void Get() + { + var _ = _dfs.BlockApi.PutAsync(_blob).Result; + var block = _dfs.BlockApi.GetAsync(Id).Result; + Assert.AreEqual(Id, block.Id.ToString()); + Assert.AreEqual(_blob, block.DataBytes); + var blob1 = new byte[_blob.Length]; + block.DataStream.Read(blob1, 0, blob1.Length); + Assert.AreEqual(_blob, blob1); + } + + [Test] + public void Stat() + { + var _ = _dfs.BlockApi.PutAsync(_blob).Result; + var info = _dfs.BlockApi.StatAsync(Id).Result; + Assert.AreEqual(Id, info.Id.ToString()); + Assert.AreEqual(5, info.Size); + } + + [Test] + public async Task Stat_Inline_CID() + { + var cts = new CancellationTokenSource(300); + var cid = new Cid + { + ContentType = "raw", + Hash = MultiHash.ComputeHash(_blob, "identity") + }; + var info = await _dfs.BlockApi.StatAsync(cid, cts.Token); + Assert.AreEqual(cid.Encode(), info.Id.ToString()); + Assert.AreEqual(5, info.Size); + } + + [Test] + public async Task Stat_Unknown() + { + const string cid = "QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rFF"; + var block = await _dfs.BlockApi.StatAsync(cid); + Assert.Null(block); + } + + [Test] + public async Task Remove() + { + var _ = _dfs.BlockApi.PutAsync(_blob).Result; + var cid = await _dfs.BlockApi.RemoveAsync(Id); + Assert.AreEqual(Id, cid.ToString()); + } + + [Test] + public async Task Remove_Inline_CID() + { + var cid = new Cid + { + ContentType = "raw", + Hash = MultiHash.ComputeHash(_blob, "identity") + }; + var removedCid = await _dfs.BlockApi.RemoveAsync(cid); + Assert.AreEqual(cid.Encode(), removedCid.Encode()); + } + + [Test] + public void Remove_Unknown() + { + ExceptionAssert.Throws(() => + { + var _ = _dfs.BlockApi.RemoveAsync("QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rFF").Result; + }); + } + + [Test] + public async Task Remove_Unknown_OK() + { + var cid = await _dfs.BlockApi.RemoveAsync("QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rFF", true); + Assert.AreEqual(null, cid); + } + + [Test] + public async Task Get_Inline_CID() + { + var cts = new CancellationTokenSource(300); + var cid = new Cid + { + ContentType = "raw", + Hash = MultiHash.ComputeHash(_blob, "identity") + }; + var block = await _dfs.BlockApi.GetAsync(cid, cts.Token); + Assert.AreEqual(cid.Encode(), block.Id.Encode()); + Assert.AreEqual(_blob.Length, block.Size); + Assert.AreEqual(_blob, block.DataBytes); + } + + [Test] + public async Task Put_Informs_Bitswap() + { + _dfs = TestDfs.GetTestDfs(null, "sha2-256"); + await _dfs.StartAsync(); + + try + { + var data = Guid.NewGuid().ToByteArray(); + var cid = new Cid + { + Hash = MultiHash.ComputeHash(data) + }; + + var cts = new CancellationTokenSource(); + cts.CancelAfter(20000); + + var wantTask = _dfs.BitSwapApi.GetAsync(cid, cts.Token); + var cid1 = await _dfs.BlockApi.PutAsync(data, cancel: cts.Token); + + Assert.AreEqual(cid, cid1); + Assert.AreEqual(cid, wantTask.Result.Id); + Assert.AreEqual(data.Length, wantTask.Result.Size); + Assert.AreEqual(data, wantTask.Result.DataBytes); + } + finally + { + await _dfs.StopAsync(); + } + } + + [Test] + public async Task Put_Informs_Dht() + { + var data = Guid.NewGuid().ToByteArray(); + await _dfs.StartAsync(); + try + { + var self = _dfs.LocalPeer; + var cid = await _dfs.BlockApi.PutAsync(data); + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(3)); + var peers = await _dfs.DhtApi.FindProvidersAsync(cid, 1, cancel: cts.Token); + Assert.AreEqual(self, peers.First()); + } + finally + { + await _dfs.StopAsync(); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BlockRepositoryApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BlockRepositoryApiTest.cs new file mode 100644 index 0000000000..4f9320d7f5 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BlockRepositoryApiTest.cs @@ -0,0 +1,102 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.TestUtils; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class BlockRepositoryApiTest + { + private IDfsService ipfs; + + public BlockRepositoryApiTest() + { + ipfs = TestDfs.GetTestDfs(); + } + + [Test] + public void Exists() { Assert.NotNull(ipfs.BlockRepositoryApi); } + + [Test] + public async Task Stats() + { + var stats = await ipfs.BlockRepositoryApi.StatisticsAsync(); + var version = await ipfs.BlockRepositoryApi.VersionAsync(); + Assert.AreEqual(stats.Version, version); + } + + [Test] + public async Task GarbageCollection() + { + var pinned = await ipfs.BlockApi.PutAsync(new byte[256], pin: true); + var unpinned = await ipfs.BlockApi.PutAsync(new byte[512]); + Assert.AreNotEqual(pinned, unpinned); + Assert.NotNull(await ipfs.BlockApi.StatAsync(pinned)); + Assert.NotNull(await ipfs.BlockApi.StatAsync(unpinned)); + + await ipfs.BlockRepositoryApi.RemoveGarbageAsync(); + Assert.NotNull(await ipfs.BlockApi.StatAsync(pinned)); + Assert.Null(await ipfs.BlockApi.StatAsync(unpinned)); + } + + [Test] + public async Task Version_Info() + { + var versions = await ipfs.BlockRepositoryApi.VersionAsync(); + Assert.NotNull(versions); + + // Assert.True(versions.ContainsKey("Version")); + // Assert.True(versions.ContainsKey("Repo")); + } + + [Test] + public async Task VersionFileMissing() + { + var versionPath = Path.Combine(ipfs.Options.Repository.ExistingFolder(), "version"); + var versionBackupPath = versionPath + ".bak"; + + try + { + if (File.Exists(versionPath)) + { + File.Move(versionPath, versionBackupPath); + } + + Assert.AreEqual("0", await ipfs.BlockRepositoryApi.VersionAsync()); + } + finally + { + if (File.Exists(versionBackupPath)) + { + File.Move(versionBackupPath, versionPath); + } + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BootstrapApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BootstrapApiTest.cs new file mode 100644 index 0000000000..38a1720b20 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/BootstrapApiTest.cs @@ -0,0 +1,125 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.TestUtils; +using MultiFormats; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + public class BootstapApiTest + { + private readonly IDfsService ipfs; + + private readonly MultiAddress somewhere = + "/ip4/127.0.0.1/tcp/4009/ipfs/QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rAQ"; + + public BootstapApiTest() + { + ipfs = TestDfs.GetTestDfs(); + } + + [Test] + public async Task Add_Remove() + { + var addr = await ipfs.BootstrapApi.AddAsync(somewhere); + Assert.NotNull(addr); + Assert.AreEqual(somewhere, addr); + var addrs = await ipfs.BootstrapApi.ListAsync(); + Assert.True(addrs.Any(a => a == somewhere)); + + addr = await ipfs.BootstrapApi.RemoveAsync(somewhere); + Assert.NotNull(addr); + Assert.AreEqual(somewhere, addr); + addrs = await ipfs.BootstrapApi.ListAsync(); + Assert.False(addrs.Any(a => a == somewhere)); + } + + [Test] + public async Task List() + { + var addrs = await ipfs.BootstrapApi.ListAsync(); + Assert.NotNull(addrs); + Assert.AreNotEqual(0, addrs.Count()); + } + + [Test] + public async Task Remove_All() + { + var original = await ipfs.BootstrapApi.ListAsync(); + await ipfs.BootstrapApi.RemoveAllAsync(); + var addrs = await ipfs.BootstrapApi.ListAsync(); + Assert.AreEqual(0, addrs.Count()); + foreach (var addr in original) + { + await ipfs.BootstrapApi.AddAsync(addr); + } + } + + [Test] + public async Task Add_Defaults() + { + var original = await ipfs.BootstrapApi.ListAsync(); + await ipfs.BootstrapApi.RemoveAllAsync(); + try + { + await ipfs.BootstrapApi.AddDefaultsAsync(); + var addrs = await ipfs.BootstrapApi.ListAsync(); + Assert.AreNotEqual(0, addrs.Count()); + } + finally + { + await ipfs.BootstrapApi.RemoveAllAsync(); + foreach (var addr in original) + { + await ipfs.BootstrapApi.AddAsync(addr); + } + } + } + + [Test] + public async Task Override_FactoryDefaults() + { + var original = ipfs.Options.Discovery.BootstrapPeers; + try + { + ipfs.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + var addrs = await ipfs.BootstrapApi.ListAsync(); + Assert.AreEqual(0, addrs.Count()); + + ipfs.Options.Discovery.BootstrapPeers = new[] + {somewhere}; + addrs = await ipfs.BootstrapApi.ListAsync(); + Assert.AreEqual(1, addrs.Count()); + Assert.AreEqual(somewhere, addrs.First()); + } + finally + { + ipfs.Options.Discovery.BootstrapPeers = original; + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/DagApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/DagApiTest.cs new file mode 100644 index 0000000000..5b88e2d43d --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/DagApiTest.cs @@ -0,0 +1,157 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Text; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.TestUtils; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class DagApiTest + { + private readonly byte[] _blob = Encoding.UTF8.GetBytes("blorb"); + private const string Blob64 = "YmxvcmI"; // base 64 encoded with no padding + private readonly IDfsService _dfs; + + public DagApiTest() + { + _dfs = TestDfs.GetTestDfs(null, "sha2-256"); + } + + [Test] + public async Task Get_Raw() + { + var cid = await _dfs.BlockApi.PutAsync(_blob, contentType: "raw"); + Assert.AreEqual("bafkreiaxnnnb7qz2focittuqq3ya25q7rcv3bqynnczfzako47346wosmu", cid.ToString()); + + var dag = await _dfs.DagApi.GetAsync(cid); + Assert.AreEqual(Blob64, (string) dag["data"]); + } + + private sealed class Name + { + public string First { get; set; } + public string Last { get; set; } + } + + [Test] + public async Task PutAndGet_JSON() + { + var expected = new JObject {["a"] = "alpha"}; + const string expectedId = "bafyreigdhej736dobd6z3jt2vxsxvbwrwgyts7e7wms6yrr46rp72uh5bu"; + var id = await _dfs.DagApi.PutAsync(expected); + Assert.NotNull(id); + Assert.AreEqual(expectedId, id.ToString()); + + var actual = await _dfs.DagApi.GetAsync(id); + Assert.NotNull(actual); + Assert.AreEqual(expected["a"], actual["a"]); + + var value = (string) await _dfs.DagApi.GetAsync(expectedId + "/a"); + Assert.AreEqual(expected["a"].ToString(), value); + } + + [Test] + public async Task PutAndGet_poco() + { + var expected = new Name {First = "John", Last = "Smith"}; + var id = await _dfs.DagApi.PutAsync(expected); + Assert.NotNull(id); + + var actual = await _dfs.DagApi.GetAsync(id); + Assert.NotNull(actual); + Assert.AreEqual(expected.First, actual.First); + Assert.AreEqual(expected.Last, actual.Last); + + var value = (string) await _dfs.DagApi.GetAsync(id.Encode() + "/Last"); + Assert.AreEqual(expected.Last, value); + } + + /// + /// @TODO hardcoded encoding types + /// + /// + [Test] + public async Task PutAndGet_poco_CidEncoding() + { + var expected = new Name {First = "John", Last = "Smith"}; + var id = await _dfs.DagApi.PutAsync(expected, encoding: "base32"); + Assert.NotNull(id); + Assert.AreEqual("base32", id.Encoding); + Assert.AreEqual(1, id.Version); + + var actual = await _dfs.DagApi.GetAsync(id); + Assert.NotNull(actual); + Assert.AreEqual(expected.First, actual.First); + Assert.AreEqual(expected.Last, actual.Last); + + var value = (string) await _dfs.DagApi.GetAsync(id.Encode() + "/Last"); + Assert.AreEqual(expected.Last, value); + } + + [Test] + public async Task PutAndGet_POCO() + { + var expected = new Name {First = "John", Last = "Smith"}; + var id = await _dfs.DagApi.PutAsync(expected); + Assert.NotNull(id); + + var actual = await _dfs.DagApi.GetAsync(id); + Assert.NotNull(actual); + Assert.AreEqual(expected.First, actual.First); + Assert.AreEqual(expected.Last, actual.Last); + + var value = (string) await _dfs.DagApi.GetAsync(id.Encode() + "/Last"); + Assert.AreEqual(expected.Last, value); + } + + [Test] + public async Task Get_Raw2() + { + var data = Encoding.UTF8.GetBytes("abc"); + var id = await _dfs.BlockApi.PutAsync(data, "raw"); + Assert.AreEqual("bafkreif2pall7dybz7vecqka3zo24irdwabwdi4wc55jznaq75q7eaavvu", id.Encode()); + + var actual = await _dfs.DagApi.GetAsync(id); + Assert.AreEqual(Convert.ToBase64String(data), (string) actual["data"]); + } + + // // https://github.com/ipfs/interface-ipfs-core/blob/master/SPEC/DAG.md + // [Test] + // [Ignore("https://github.com/richardschneider/net-ipfs-engine/issues/30")] + // public async Task Example1() + // { + // Cid expected = + // "zBwWX9ecx5F4X54WAjmFLErnBT6ByfNxStr5ovowTL7AhaUR98RWvXPS1V3HqV1qs3r5Ec5ocv7eCdbqYQREXNUfYNuKG"; + // var obj = new {simple = "object"}; + // var cid = await ipfs.Dag.PutAsync(obj, multiHash: "sha3-512"); + // Assert.AreEqual((string) expected, (string) cid); + // } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/DhtApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/DhtApiTest.cs new file mode 100644 index 0000000000..f174afdb28 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/DhtApiTest.cs @@ -0,0 +1,112 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using Catalyst.Abstractions.Dfs; +using Catalyst.TestUtils; +using Lib.P2P; +using MultiFormats; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + public class DhtApiTest + { + private IDfsService ipfs; + private MultiHash _locaId; + + public DhtApiTest() + { + ipfs = TestDfs.GetTestDfs(); + + _locaId = ipfs.LocalPeer.Id; + } + + [Test] + public void Local_Info() + { + var peer = ipfs.DhtApi.FindPeerAsync(_locaId).GetAwaiter().GetResult(); + Assert.IsInstanceOf(typeof(Peer), peer); + Assert.NotNull(peer.Addresses); + Assert.That(peer.AgentVersion, Does.StartWith("net-ipfs/")); + Assert.NotNull(peer.Id); + Assert.That(peer.ProtocolVersion, Does.StartWith("ipfs/")); + Assert.NotNull(peer.PublicKey); + Assert.AreEqual(_locaId, peer.Id); + Assert.True(peer.IsValid()); + } + + [Test] + public void Mars_Info() + { + const string marsId = "QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + var marsAddr = $"/ip6/::1/p2p/{marsId}"; + var swarm = ipfs.SwarmService; + var mars = swarm.RegisterPeerAddress(marsAddr); + + var peer = ipfs.DhtApi.FindPeerAsync(marsId).GetAwaiter().GetResult(); + Assert.AreEqual(mars.Id, peer.Id); + Assert.AreEqual(mars.Addresses.First(), peer.Addresses.First()); + } + + // [Test] + // [Ignore("https://github.com/richardschneider/net-ipfs-engine/issues/74#issuecomment-500668261")] + // public async Task Mars_Info() + // { + // var marsId = "QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + // var ipfs = TestFixture.Ipfs; + // await ipfs.StartAsync(); + // try + // { + // var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + // var mars = await ipfs.Dht.FindPeerAsync(marsId, cts.Token); + // Assert.AreEqual(marsId, mars.Id); + // Assert.NotNull(mars.Addresses); + // Assert.True(mars.IsValid()); + // } + // finally + // { + // await ipfs.StopAsync(); + // } + // } + + // [Test] + // [Ignore("https://github.com/richardschneider/net-ipfs-engine/issues/74")] + // public async Task FindProvider() + // { + // var folder = "QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv"; + // var ipfs = TestFixture.Ipfs; + // await ipfs.StartAsync(); + // try + // { + // var cts = new CancellationTokenSource(TimeSpan.FromMinutes(1)); + // var providers = await ipfs.Dht.FindProvidersAsync(folder, 1, null, cts.Token); + // Assert.AreEqual(1, providers.Count()); + // } + // finally + // { + // await ipfs.StopAsync(); + // } + // } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/DnsApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/DnsApiTest.cs new file mode 100644 index 0000000000..ea4d6e941a --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/DnsApiTest.cs @@ -0,0 +1,62 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + //public class DnsApiTest + //{ + // private IDfsService ipfs; + + // public DnsApiTest(TestContext output) + // { + // ipfs = TestDfs.GetTestDfs(output); + // } + + // [Test] + // public async Task Resolve() + // { + // var path = await ipfs.DnsApi.ResolveAsync("ipfs.io"); + // Assert.NotNull(path); + // } + + // [Test] + // public void Resolve_NoLink() + // { + // ExceptionAssert.Throws(() => + // { + // var _ = ipfs.DnsApi.ResolveAsync("google.com").Result; + // }); + // } + + // [Test] + // public async Task Resolve_Recursive() + // { + // var path = await ipfs.DnsApi.ResolveAsync("ipfs.io", true); + // Assert.StartsWith("/ipfs/", path); + // } + //} +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/FileSystemApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/FileSystemApiTest.cs new file mode 100644 index 0000000000..9e02586e9a --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/FileSystemApiTest.cs @@ -0,0 +1,1009 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Modules.Dfs.UnixFs; +using Catalyst.TestUtils; +using FluentAssertions; +using ICSharpCode.SharpZipLib.Tar; +using Lib.P2P; +using Lib.P2P.Cryptography; +using MultiFormats; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + public class FileSystemApiTest + { + private IDfsService ipfs; + + [SetUp] + public void Init() + { + ipfs = TestDfs.GetTestDfs(null, "sha2-256", "rsa"); + } + + [Test] + public async Task AddText() + { + var node = (UnixFsNode) await ipfs.UnixFsApi.AddTextAsync("hello world"); + Assert.AreEqual("Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD", node.Id.ToString()); + Assert.AreEqual("", node.Name); + Assert.AreEqual(0, node.Links.Count()); + + var text = await ipfs.UnixFsApi.ReadAllTextAsync(node.Id); + Assert.AreEqual("hello world", text); + + var actual = await ipfs.UnixFsApi.ListFileAsync(node.Id); + Assert.AreEqual(node.Id, actual.Id); + Assert.AreEqual(node.IsDirectory, actual.IsDirectory); + Assert.AreEqual(node.Links.Count(), actual.Links.Count()); + Assert.AreEqual(node.Size, actual.Size); + } + + [Test] + public async Task AddEmptyText() + { + var node = (UnixFsNode) await ipfs.UnixFsApi.AddTextAsync(""); + Assert.AreEqual("QmbFMke1KXqnYyBBWxB74N4c5SBnJMVAiMNRcGu6x1AwQH", node.Id.ToString()); + Assert.AreEqual("", node.Name); + Assert.AreEqual(0, node.Links.Count()); + + var text = await ipfs.UnixFsApi.ReadAllTextAsync(node.Id); + Assert.AreEqual("", text); + + var actual = await ipfs.UnixFsApi.ListFileAsync(node.Id); + Assert.AreEqual(node.Id, actual.Id); + Assert.AreEqual(node.IsDirectory, actual.IsDirectory); + Assert.AreEqual(node.Links.Count(), actual.Links.Count()); + Assert.AreEqual(node.Size, actual.Size); + } + + [Test] + public async Task AddEmpty_Check_Object() + { + // see https://github.com/ipfs/js-ipfs-unixfs/pull/25 + var node = await ipfs.UnixFsApi.AddTextAsync(""); + var block = await ipfs.ObjectApi.GetAsync(node.Id); + var expected = new byte[] {0x08, 0x02, 0x18, 0x00}; + Assert.AreEqual(node.Id, block.Id); + Assert.AreEqual(expected, block.DataBytes); + } + + [Test] + public async Task AddDuplicateWithPin() + { + var options = new AddFileOptions + { + Pin = true + }; + var node = await ipfs.UnixFsApi.AddTextAsync("hello world", options); + Assert.AreEqual("Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD", node.Id.ToString()); + var pins = await ipfs.PinApi.ListAsync(); + pins.ToArray().Should().Contain(node.Id); + + options.Pin = false; + node = await ipfs.UnixFsApi.AddTextAsync("hello world", options); + Assert.AreEqual("Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD", node.Id.ToString()); + Assert.AreEqual(0, node.Links.Count()); + pins = await ipfs.PinApi.ListAsync(); + pins.ToArray().Should().NotContain(node.Id); + } + + [Test] + public async Task Add_SizeChunking() + { + var options = new AddFileOptions {ChunkSize = 3, Pin = true}; + var node = await ipfs.UnixFsApi.AddTextAsync("hello world", options); + var links = node.Links.ToArray(); + Assert.AreEqual("QmVVZXWrYzATQdsKWM4knbuH5dgHFmrRqW3nJfDgdWrBjn", node.Id.ToString()); + Assert.AreEqual(false, node.IsDirectory); + Assert.AreEqual(4, links.Length); + Assert.AreEqual("QmevnC4UDUWzJYAQtUSQw4ekUdqDqwcKothjcobE7byeb6", links[0].Id.ToString()); + Assert.AreEqual("QmTdBogNFkzUTSnEBQkWzJfQoiWbckLrTFVDHFRKFf6dcN", links[1].Id.ToString()); + Assert.AreEqual("QmPdmF1n4di6UwsLgW96qtTXUsPkCLN4LycjEUdH9977d6", links[2].Id.ToString()); + Assert.AreEqual("QmXh5UucsqF8XXM8UYQK9fHXsthSEfi78kewr8ttpPaLRE", links[3].Id.ToString()); + + var text = await ipfs.UnixFsApi.ReadAllTextAsync(node.Id); + Assert.AreEqual("hello world", text); + } + + [Test] + public async Task StreamBehaviour() + { + var options = new AddFileOptions + { + ChunkSize = 3, + Pin = true, + }; + var node = await ipfs.UnixFsApi.AddTextAsync("hello world", options); + var stream = await ipfs.UnixFsApi.ReadFileAsync(node.Id); + Assert.AreEqual(11, stream.Length); + Assert.True(stream.CanRead); + Assert.False(stream.CanWrite); + Assert.True(stream.CanSeek); + } + + [Test] + public async Task Add_HashAlgorithm() + { + var options = new AddFileOptions + { + Hash = "blake2b-256", + RawLeaves = true + }; + var node = await ipfs.UnixFsApi.AddTextAsync("hello world", options); + Assert.AreEqual("bafk2bzaceaswza5ss4iu2ia3galz6pyo6dfm5f4dmiw2lf2de22dmf4k533ba", node.Id.ToString()); + + var text = await ipfs.UnixFsApi.ReadAllTextAsync(node.Id); + Assert.AreEqual("hello world", text); + } + + [Test] + public void AddFile() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, "hello world"); + try + { + var node = (UnixFsNode) ipfs.UnixFsApi.AddFileAsync(path).Result; + Assert.AreEqual("Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD", node.Id.ToString()); + Assert.AreEqual(0, node.Links.Count()); + Assert.AreEqual(Path.GetFileName(path), node.Name); + } + finally + { + File.Delete(path); + } + } + + [Test] + public void AddFile_CidEncoding() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, "hello world"); + try + { + var options = new AddFileOptions + { + Encoding = "base32" + }; + var node = ipfs.UnixFsApi.AddFileAsync(path, options).Result; + Assert.AreEqual("base32", node.Id.Encoding); + Assert.AreEqual(1, node.Id.Version); + Assert.AreEqual(0, node.Links.Count()); + + var text = ipfs.UnixFsApi.ReadAllTextAsync(node.Id).Result; + Assert.AreEqual("hello world", text); + } + finally + { + File.Delete(path); + } + } + + [Test] + public void AddFile_Large() + { + AddFile(); // warm up + + var path = "star_trails.mp4"; + var stopWatch = new Stopwatch(); + stopWatch.Start(); + var node = ipfs.UnixFsApi.AddFileAsync(path).Result; + stopWatch.Stop(); + + // _testOutputHelper.WriteLine("Add file took {0} seconds.", stopWatch.Elapsed.TotalSeconds); + + Assert.AreEqual("QmeZkAUfUFPq5YWGBan2ZYNd9k59DD1xW62pGJrU3C6JRo", node.Id.ToString()); + + var k = 8 * 1024; + var buffer1 = new byte[k]; + var buffer2 = new byte[k]; + stopWatch.Restart(); + using (var localStream = new FileStream(path, FileMode.Open, FileAccess.Read)) + { + using var ipfsStream = ipfs.UnixFsApi.ReadFileAsync(node.Id).Result; + while (true) + { + var n1 = localStream.Read(buffer1, 0, k); + var n2 = ipfsStream.Read(buffer2, 0, k); + Assert.AreEqual(n1, n2); + if (n1 == 0) + { + break; + } + + for (var i = 0; i < n1; ++i) + { + if (buffer1[i] != buffer2[i]) + { + throw new Exception("data not the same"); + } + } + } + } + + stopWatch.Stop(); + + // _testOutputHelper.WriteLine("Readfile file took {0} seconds.", stopWatch.Elapsed.TotalSeconds); + } + + /// + [Test] + public void AddFile_Larger() + { + AddFile(); // warm up + + var path = "starx2.mp4"; + var stopWatch = new Stopwatch(); + stopWatch.Start(); + var node = ipfs.UnixFsApi.AddFileAsync(path).Result; + stopWatch.Stop(); + TestContext.WriteLine("Add file took {0} seconds.", stopWatch.Elapsed.TotalSeconds); + + Assert.AreEqual("QmeFhfB4g2GFbxYb7usApWzq8uC1vmuxJajFpiJiT5zLoy", node.Id.ToString()); + + const int k = 8 * 1024; + var buffer1 = new byte[k]; + var buffer2 = new byte[k]; + stopWatch.Restart(); + using (var localStream = new FileStream(path, FileMode.Open, FileAccess.Read)) + { + using var ipfsStream = ipfs.UnixFsApi.ReadFileAsync(node.Id).Result; + while (true) + { + var n1 = localStream.Read(buffer1, 0, k); + var n2 = ipfsStream.Read(buffer2, 0, k); + Assert.AreEqual(n1, n2); + if (n1 == 0) + { + break; + } + + for (var i = 0; i < n1; ++i) + { + if (buffer1[i] != buffer2[i]) + { + throw new Exception("data not the same"); + } + } + } + } + + stopWatch.Stop(); + TestContext.WriteLine("Readfile file took {0} seconds.", stopWatch.Elapsed.TotalSeconds); + } + + [Test] + public async Task AddFile_Wrap() + { + const string path = "hello.txt"; + File.WriteAllText(path, "hello world"); + try + { + var options = new AddFileOptions + { + Wrap = true + }; + var node = await ipfs.UnixFsApi.AddFileAsync(path, options); + Assert.AreEqual("QmNxvA5bwvPGgMXbmtyhxA1cKFdvQXnsGnZLCGor3AzYxJ", node.Id.ToString()); + Assert.AreEqual(true, node.IsDirectory); + Assert.AreEqual(1, node.Links.Count()); + Assert.AreEqual("hello.txt", node.Links.First().Name); + Assert.AreEqual("Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD", node.Links.First().Id.ToString()); + Assert.AreEqual(19, node.Links.First().Size); + } + finally + { + File.Delete(path); + } + } + + [Test] + public async Task Add_Raw() + { + var options = new AddFileOptions + { + RawLeaves = true + }; + var node = await ipfs.UnixFsApi.AddTextAsync("hello world", options); + Assert.AreEqual("bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e", node.Id.ToString()); + Assert.AreEqual(11, node.Size); + Assert.AreEqual(0, node.Links.Count()); + Assert.AreEqual(false, node.IsDirectory); + + var text = await ipfs.UnixFsApi.ReadAllTextAsync(node.Id); + Assert.AreEqual("hello world", text); + } + + [Test] + public async Task Add_Inline() + { + var original = ipfs.Options.Block.AllowInlineCid; + try + { + ipfs.Options.Block.AllowInlineCid = true; + + var node = await ipfs.UnixFsApi.AddTextAsync("hiya"); + Assert.AreEqual(1, node.Id.Version); + Assert.True(node.Id.Hash.IsIdentityHash); + Assert.AreEqual(4, node.Size); + Assert.AreEqual(0, node.Links.Count()); + Assert.AreEqual(false, node.IsDirectory); + Assert.AreEqual("bafyaadakbieaeeqenbuxsyiyaq", node.Id.Encode()); + var text = await ipfs.UnixFsApi.ReadAllTextAsync(node.Id); + Assert.AreEqual("hiya", text); + } + finally + { + ipfs.Options.Block.AllowInlineCid = original; + } + } + + [Test] + public async Task Add_RawAndChunked() + { + var options = new AddFileOptions + { + RawLeaves = true, + ChunkSize = 3 + }; + var node = await ipfs.UnixFsApi.AddTextAsync("hello world", options); + var links = node.Links.ToArray(); + Assert.AreEqual("QmUuooB6zEhMmMaBvMhsMaUzar5gs5KwtVSFqG4C1Qhyhs", node.Id.ToString()); + Assert.AreEqual(false, node.IsDirectory); + Assert.AreEqual(4, links.Length); + Assert.AreEqual("bafkreigwvapses57f56cfow5xvoua4yowigpwcz5otqqzk3bpcbbjswowe", links[0].Id.ToString()); + Assert.AreEqual("bafkreiew3cvfrp2ijn4qokcp5fqtoknnmr6azhzxovn6b3ruguhoubkm54", links[1].Id.ToString()); + Assert.AreEqual("bafkreibsybcn72tquh2l5zpim2bba4d2kfwcbpzuspdyv2breaq5efo7tq", links[2].Id.ToString()); + Assert.AreEqual("bafkreihfuch72plvbhdg46lef3n5zwhnrcjgtjywjryyv7ffieyedccchu", links[3].Id.ToString()); + + var text = await ipfs.UnixFsApi.ReadAllTextAsync(node.Id); + Assert.AreEqual("hello world", text); + } + + [Test] + public async Task Add_Protected() + { + var options = new AddFileOptions + { + ProtectionKey = "self" + }; + var node = await ipfs.UnixFsApi.AddTextAsync("hello world", options); + Assert.AreEqual("cms", node.Id.ContentType); + Assert.AreEqual(0, node.Links.Count()); + Assert.AreEqual(false, node.IsDirectory); + + var text = await ipfs.UnixFsApi.ReadAllTextAsync(node.Id); + Assert.AreEqual("hello world", text); + } + + [Test] + public async Task Add_Protected_Chunked() + { + var options = new AddFileOptions + { + ProtectionKey = "self", + ChunkSize = 3 + }; + var node = await ipfs.UnixFsApi.AddTextAsync("hello world", options); + Assert.AreEqual(4, node.Links.Count()); + Assert.AreEqual(false, node.IsDirectory); + + var text = await ipfs.UnixFsApi.ReadAllTextAsync(node.Id); + Assert.AreEqual("hello world", text); + } + + [Test] + public async Task Add_OnlyHash() + { + var nodes = new[] + { + "QmVVZXWrYzATQdsKWM4knbuH5dgHFmrRqW3nJfDgdWrBjn", + "QmevnC4UDUWzJYAQtUSQw4ekUdqDqwcKothjcobE7byeb6", + "QmTdBogNFkzUTSnEBQkWzJfQoiWbckLrTFVDHFRKFf6dcN", + "QmPdmF1n4di6UwsLgW96qtTXUsPkCLN4LycjEUdH9977d6", + "QmXh5UucsqF8XXM8UYQK9fHXsthSEfi78kewr8ttpPaLRE" + }; + foreach (var n in nodes) + { + await ipfs.BlockApi.RemoveAsync(n, ignoreNonexistent: true); + } + + var options = new AddFileOptions + { + ChunkSize = 3, + OnlyHash = true, + }; + var node = await ipfs.UnixFsApi.AddTextAsync("hello world", options); + var links = node.Links.ToArray(); + Assert.AreEqual(nodes[0], node.Id.ToString()); + Assert.AreEqual(nodes.Length - 1, links.Length); + for (var i = 0; i < links.Length; ++i) + { + Assert.AreEqual(nodes[i + 1], links[i].Id.ToString()); + } + + // TODO: Need a method to test that the CId is not held locally. + //foreach (var n in nodes) + //{ + // Assert.Null(await ipfs.Block.StatAsync(n)); + //} + } + + [Test] + public async Task ReadWithOffset() + { + const string text = "hello world"; + var options = new AddFileOptions + { + ChunkSize = 3 + }; + var node = await ipfs.UnixFsApi.AddTextAsync(text, options); + + for (var offset = 0; offset <= text.Length; ++offset) + { + await using (var data = await ipfs.UnixFsApi.ReadFileAsync(node.Id, offset)) + { + using var reader = new StreamReader(data); + { + var readData = reader.ReadToEnd(); + Assert.AreEqual(text.Substring(offset), readData); + } + } + } + } + + [Test] + public async Task Read_RawWithLength() + { + const string text = "hello world"; + var options = new AddFileOptions + { + RawLeaves = true + }; + var node = await ipfs.UnixFsApi.AddTextAsync(text, options); + + for (var offset = 0; offset < text.Length; ++offset) + { + for (var length = text.Length + 1; 0 < length; --length) + { + await using (var data = await ipfs.UnixFsApi.ReadFileAsync(node.Id, offset, length)) + { + using var reader = new StreamReader(data); + { + var readData = reader.ReadToEnd(); + Assert.AreEqual(text.Substring(offset, Math.Min(11 - offset, length)), readData); + } + } + } + } + } + + [Test] + public async Task Read_ChunkedWithLength() + { + const string text = "hello world"; + var options = new AddFileOptions + { + ChunkSize = 3 + }; + var node = await ipfs.UnixFsApi.AddTextAsync(text, options); + + for (var length = text.Length + 1; 0 < length; --length) + { + await using (var data = await ipfs.UnixFsApi.ReadFileAsync(node.Id, 0, length)) + { + using var reader = new StreamReader(data); + { + var readData = reader.ReadToEnd(); + Assert.AreEqual(text.Substring(0, Math.Min(11, length)), readData); + } + } + } + } + + [Test] + public async Task Read_ProtectedWithLength() + { + const string text = "hello world"; + var options = new AddFileOptions + { + ProtectionKey = "self" + }; + var node = await ipfs.UnixFsApi.AddTextAsync(text, options); + + for (var offset = 0; offset < text.Length; ++offset) + { + for (var length = text.Length + 1; 0 < length; --length) + { + await using (var data = await ipfs.UnixFsApi.ReadFileAsync(node.Id, offset, length)) + { + using (var reader = new StreamReader(data)) + { + var readData = reader.ReadToEnd(); + Assert.AreEqual(text.Substring(offset, Math.Min(11 - offset, length)), readData); + } + } + } + } + } + + [Test] + public async Task Read_ProtectedChunkedWithLength() + { + const string text = "hello world"; + var options = new AddFileOptions + { + ChunkSize = 3, + ProtectionKey = "self" + }; + var node = await ipfs.UnixFsApi.AddTextAsync(text, options); + + for (var offset = 0; offset < text.Length; ++offset) + { + for (var length = text.Length + 1; 0 < length; --length) + { + await using (var data = await ipfs.UnixFsApi.ReadFileAsync(node.Id, offset, length)) + { + using (var reader = new StreamReader(data)) + { + var readData = reader.ReadToEnd(); + Assert.AreEqual(text.Substring(offset, Math.Min(11 - offset, length)), readData); + } + } + } + } + } + + [Test] + public async Task Read_ProtectedMissingKey() + { + const string text = "hello world"; + var key = await ipfs.KeyApi.CreateAsync("alice", "rsa", 512); + try + { + var options = new AddFileOptions {ProtectionKey = key.Name}; + var node = await ipfs.UnixFsApi.AddTextAsync(text, options); + Assert.AreEqual(text, await ipfs.UnixFsApi.ReadAllTextAsync(node.Id)); + + await ipfs.KeyApi.RemoveAsync(key.Name); + ExceptionAssert.Throws(() => + { + var _ = ipfs.UnixFsApi.ReadAllTextAsync(node.Id).Result; + }); + } + finally + { + await ipfs.KeyApi.RemoveAsync(key.Name); + } + } + + [Test] + [Ignore("To be fixed in issue #1241")] + public async Task AddFile_WithProgress() + { + var path = Path.GetTempFileName(); + File.WriteAllText(path, "hello world"); + try + { + TransferProgress lastProgress = null; + var options = new AddFileOptions + { + ChunkSize = 3, + Progress = new Progress(t => { lastProgress = t; }) + }; + + await ipfs.UnixFsApi.AddFileAsync(path, options); + + // Progress reports get posted on another synchronisation context + // so they can come in later. + var stop = DateTime.Now.AddSeconds(3); + while (DateTime.Now < stop && lastProgress?.Bytes == 11UL) + { + await Task.Delay(10); + } + + Assert.AreEqual(11UL, lastProgress.Bytes); + Assert.AreEqual(Path.GetFileName(path), lastProgress.Name); + } + finally + { + File.Delete(path); + } + } + + [Test] + public void AddDirectory() + { + var temp = MakeTemp(); + try + { + var dir = ipfs.UnixFsApi.AddDirectoryAsync(temp, false).Result; + Assert.True(dir.IsDirectory); + + var files = dir.Links.ToArray(); + Assert.AreEqual(2, files.Length); + Assert.AreEqual("alpha.txt", files[0].Name); + Assert.AreEqual("beta.txt", files[1].Name); + + Assert.AreEqual("alpha", ipfs.UnixFsApi.ReadAllTextAsync(files[0].Id).Result); + Assert.AreEqual("beta", ipfs.UnixFsApi.ReadAllTextAsync(files[1].Id).Result); + + Assert.AreEqual("alpha", ipfs.UnixFsApi.ReadAllTextAsync(dir.Id + "/alpha.txt").Result); + Assert.AreEqual("beta", ipfs.UnixFsApi.ReadAllTextAsync(dir.Id + "/beta.txt").Result); + } + finally + { + Directory.Delete(temp, true); + } + } + + [Test] + public void AddDirectoryRecursive() + { + var temp = MakeTemp(); + try + { + var dir = ipfs.UnixFsApi.AddDirectoryAsync(temp).Result; + Assert.True(dir.IsDirectory); + + var files = dir.Links.ToArray(); + Assert.AreEqual(3, files.Length); + Assert.AreEqual("alpha.txt", files[0].Name); + Assert.AreEqual("beta.txt", files[1].Name); + Assert.AreEqual("x", files[2].Name); + Assert.AreNotEqual(0, files[0].Size); + Assert.AreNotEqual(0, files[1].Size); + + var rootFiles = ipfs.UnixFsApi.ListFileAsync(dir.Id).Result.Links.ToArray(); + Assert.AreEqual(3, rootFiles.Length); + Assert.AreEqual("alpha.txt", rootFiles[0].Name); + Assert.AreEqual("beta.txt", rootFiles[1].Name); + Assert.AreEqual("x", rootFiles[2].Name); + + var xfiles = ipfs.UnixFsApi.ListFileAsync(rootFiles[2].Id).Result.Links.ToArray(); + Assert.AreEqual(2, xfiles.Length); + Assert.AreEqual("x.txt", xfiles[0].Name); + Assert.AreEqual("y", xfiles[1].Name); + + var yfiles = ipfs.UnixFsApi.ListFileAsync(xfiles[1].Id).Result.Links.ToArray(); + Assert.AreEqual(1, yfiles.Length); + Assert.AreEqual("y.txt", yfiles[0].Name); + + Assert.AreEqual("x", ipfs.UnixFsApi.ReadAllTextAsync(dir.Id + "/x/x.txt").Result); + Assert.AreEqual("y", ipfs.UnixFsApi.ReadAllTextAsync(dir.Id + "/x/y/y.txt").Result); + } + finally + { + Directory.Delete(temp, true); + } + } + + [Test] + public void AddDirectory_WithHashAlgorithm() + { + const string alg = "keccak-512"; + var options = new AddFileOptions {Hash = alg}; + var temp = MakeTemp(); + try + { + var dir = ipfs.UnixFsApi.AddDirectoryAsync(temp, false, options).Result; + Assert.True(dir.IsDirectory); + Assert.AreEqual(alg, dir.Id.Hash.Algorithm.Name); + + foreach (var link in dir.Links) + { + Assert.AreEqual(alg, link.Id.Hash.Algorithm.Name); + } + } + finally + { + Directory.Delete(temp, true); + } + } + + [Test] + public void AddDirectory_WithCidEncoding() + { + var encoding = "base32z"; + var options = new AddFileOptions {Encoding = encoding}; + var temp = MakeTemp(); + try + { + var dir = ipfs.UnixFsApi.AddDirectoryAsync(temp, false, options).Result; + Assert.True(dir.IsDirectory); + Assert.AreEqual(encoding, dir.Id.Encoding); + + foreach (var link in dir.Links) + { + Assert.AreEqual(encoding, link.Id.Encoding); + } + } + finally + { + Directory.Delete(temp, true); + } + } + + [Test] + public async Task AddDirectoryRecursive_ObjectLinks() + { + var temp = MakeTemp(); + try + { + var dir = await ipfs.UnixFsApi.AddDirectoryAsync(temp); + Assert.True(dir.IsDirectory); + + var cid = dir.Id; + var i = 0; + var allLinks = new List(); + while (cid != null) + { + var links = await ipfs.ObjectApi.LinksAsync(cid); + allLinks.AddRange(links); + cid = i < allLinks.Count ? allLinks[i++].Id : null; + } + + Assert.AreEqual(6, allLinks.Count); + Assert.AreEqual("alpha.txt", allLinks[0].Name); + Assert.AreEqual("beta.txt", allLinks[1].Name); + Assert.AreEqual("x", allLinks[2].Name); + Assert.AreEqual("x.txt", allLinks[3].Name); + Assert.AreEqual("y", allLinks[4].Name); + Assert.AreEqual("y.txt", allLinks[5].Name); + } + finally + { + Directory.Delete(temp, true); + } + } + + // [Test] + // [Ignore("https://github.com/richardschneider/net-ipfs-engine/issues/74")] + // public async Task ReadTextFromNetwork() + // { + // var ipfs = TestFixture.Ipfs; + // await ipfs.StartAsync(); + // + // try + // { + // var folder = "QmS4ustL54uo8FzR9455qaxZwuMiUhyvMcX9Ba8nUH4uVv"; + // await ipfs.Block.RemoveAsync(folder, true); + // + // var cts = new CancellationTokenSource(TimeSpan.FromMinutes(2)); + // var text = await ipfs.FileSystem.ReadAllTextAsync($"{folder}/about", cts.Token); + // Assert.Contains(text, "IPFS -- Inter-Planetary File system"); + // } + // finally + // { + // await ipfs.StopAsync(); + // } + // } + + [Test] + public async Task Read_From_OtherNode() + { + using (var a = TestDfs.GetTestDfs()) + { + using (var b = TestDfs.GetTestDfs()) + { + using (var c = TestDfs.GetTestDfs()) + { + var psk = new PreSharedKey().Generate(); + + // Start bootstrap node. + b.Options.Discovery.DisableMdns = true; + b.Options.Swarm.MinConnections = 0; + b.Options.Swarm.PrivateNetworkKey = psk; + b.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + await b.StartAsync(); + var bootstrapPeers = new[] + { + b.LocalPeer.Addresses.First() + }; + TestContext.WriteLine($"B is {b.LocalPeer}"); + + // Node that has the content. + c.Options.Discovery.DisableMdns = true; + c.Options.Swarm.MinConnections = 0; + c.Options.Swarm.PrivateNetworkKey = psk; + c.Options.Discovery.BootstrapPeers = bootstrapPeers; + await c.StartAsync(); + await c.SwarmApi.ConnectAsync(bootstrapPeers[0]); + TestContext.WriteLine($"C is {c.LocalPeer}"); + + var fsn = await c.UnixFsApi.AddTextAsync("some content"); + var cid = fsn.Id; + + // Node that reads the content. + a.Options.Discovery.DisableMdns = true; + a.Options.Swarm.MinConnections = 0; + a.Options.Swarm.PrivateNetworkKey = psk; + a.Options.Discovery.BootstrapPeers = bootstrapPeers; + await a.StartAsync(); + TestContext.WriteLine($"A is {a.LocalPeer}"); + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var content = await a.UnixFsApi.ReadAllTextAsync(cid, cts.Token); + Assert.AreEqual("some content", content); + } + } + } + } + + [Test] + public async Task GetTar() + { + var temp = MakeTemp(); + try + { + var dir = ipfs.UnixFsApi.AddDirectoryAsync(temp).Result; + var dirid = dir.Id.Encode(); + + var tar = await ipfs.UnixFsApi.GetAsync(dir.Id); + var archive = TarArchive.CreateInputTarArchive(tar); + var files = new List(); + archive.ProgressMessageEvent += (a, e, m) => { files.Add(e.Name); }; + archive.ListContents(); + + Assert.AreEqual($"{dirid}", files[0]); + Assert.AreEqual($"{dirid}/alpha.txt", files[1]); + Assert.AreEqual($"{dirid}/beta.txt", files[2]); + Assert.AreEqual($"{dirid}/x", files[3]); + Assert.AreEqual($"{dirid}/x/x.txt", files[4]); + Assert.AreEqual($"{dirid}/x/y", files[5]); + Assert.AreEqual($"{dirid}/x/y/y.txt", files[6]); + } + finally + { + Directory.Delete(temp, true); + } + } + + [Test] + public async Task GetTar_RawLeaves() + { + var temp = MakeTemp(); + try + { + var options = new AddFileOptions + { + RawLeaves = true + }; + var dir = ipfs.UnixFsApi.AddDirectoryAsync(temp, true, options).Result; + var dirid = dir.Id.Encode(); + + var tar = await ipfs.UnixFsApi.GetAsync(dir.Id); + var archive = TarArchive.CreateInputTarArchive(tar); + var files = new List(); + archive.ProgressMessageEvent += (a, e, m) => { files.Add(e.Name); }; + archive.ListContents(); + + Assert.AreEqual($"{dirid}", files[0]); + Assert.AreEqual($"{dirid}/alpha.txt", files[1]); + Assert.AreEqual($"{dirid}/beta.txt", files[2]); + Assert.AreEqual($"{dirid}/x", files[3]); + Assert.AreEqual($"{dirid}/x/x.txt", files[4]); + Assert.AreEqual($"{dirid}/x/y", files[5]); + Assert.AreEqual($"{dirid}/x/y/y.txt", files[6]); + } + finally + { + Directory.Delete(temp, true); + } + } + + [Test] + public async Task GetTar_EmptyDirectory() + { + var temp = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + Directory.CreateDirectory(temp); + try + { + var dir = ipfs.UnixFsApi.AddDirectoryAsync(temp).Result; + var _ = dir.Id.Encode(); + + var tar = await ipfs.UnixFsApi.GetAsync(dir.Id); + Assert.AreEqual(3 * 512, tar.Length); + } + finally + { + Directory.Delete(temp, true); + } + } + + [Test] + public async Task Isssue108() + { + var options = new AddFileOptions + { + Hash = "keccak-256", + RawLeaves = true + }; + var node = await ipfs.UnixFsApi.AddTextAsync("hello world", options); + var other = await ipfs.UnixFsApi.ListFileAsync(node.Id); + Assert.AreEqual(node.Id, other.Id); + Assert.AreEqual(node.IsDirectory, other.IsDirectory); + Assert.AreEqual(node.Size, other.Size); + } + + [Test] + public async Task Read_SameFile_DifferentCids() + { + const string text = "\"hello world\" \r\n"; + var node = await ipfs.UnixFsApi.AddTextAsync(text); + var cids = new[] + { + node.Id, + new Cid + { + ContentType = node.Id.ContentType, + Version = 1, + Encoding = node.Id.Encoding, + Hash = node.Id.Hash, + }, + new Cid + { + ContentType = node.Id.ContentType, + Version = 1, + Encoding = "base32", + Hash = node.Id.Hash, + }, + }; + foreach (var cid in cids) + { + using (var cts = new CancellationTokenSource(3000)) + { + var got = await ipfs.UnixFsApi.ReadAllTextAsync(cid, cts.Token); + Assert.AreEqual(text, got); + } + } + } + + internal static string MakeTemp() + { + var temp = Path.Combine(Path.GetTempPath(), Path.GetRandomFileName()); + var x = Path.Combine(temp, "x"); + var xy = Path.Combine(x, "y"); + Directory.CreateDirectory(temp); + Directory.CreateDirectory(x); + Directory.CreateDirectory(xy); + + File.WriteAllText(Path.Combine(temp, "alpha.txt"), "alpha"); + File.WriteAllText(Path.Combine(temp, "beta.txt"), "beta"); + File.WriteAllText(Path.Combine(x, "x.txt"), "x"); + File.WriteAllText(Path.Combine(xy, "y.txt"), "y"); + return temp; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/NameApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/NameApiTest.cs new file mode 100644 index 0000000000..888efa224e --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/NameApiTest.cs @@ -0,0 +1,124 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.TestUtils; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + public class NameApiTest + { + private IDfsService ipfs; + + public NameApiTest() + { + ipfs = TestDfs.GetTestDfs(null, "sha2-256"); + } + + [Test] + public async Task Resolve_Cid() + { + var actual = await ipfs.NameApi.ResolveAsync("QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao"); + Assert.AreEqual("/ipfs/QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao", actual); + + actual = await ipfs.NameApi.ResolveAsync("/ipfs/QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao"); + Assert.AreEqual("/ipfs/QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao", actual); + } + + [Test] + public async Task Resolve_Cid_Path() + { + var temp = FileSystemApiTest.MakeTemp(); + try + { + var dir = await ipfs.UnixFsApi.AddDirectoryAsync(temp); + var name = "/ipfs/" + dir.Id.Encode() + "/x/y/y.txt"; + Assert.AreEqual("/ipfs/QmTwEE2eSyzcvUctxP2negypGDtj7DQDKVy8s3Rvp6y6Pc", + await ipfs.NameApi.ResolveAsync(name)); + } + finally + { + Directory.Delete(temp, true); + } + } + + [Test] + public void Resolve_Cid_Invalid() + { + ExceptionAssert.Throws(() => + { + var _ = ipfs.NameApi.ResolveAsync("QmHash").Result; + }); + } + + [Test] + public async Task Resolve_DnsLink() + { + var iopath = await ipfs.NameApi.ResolveAsync("ipfs.io"); + Assert.NotNull(iopath); + + var path = await ipfs.NameApi.ResolveAsync("/ipns/ipfs.io"); + Assert.AreEqual(iopath, path); + } + + [Test] + public async Task Resolve_DnsLink_Recursive() + { + var path = await ipfs.NameApi.ResolveAsync("/ipns/ipfs.io/media", true); + Assert.That(path, Does.StartWith("/ipfs/")); + Assert.That(path, Does.EndWith("/media")); + + path = await ipfs.NameApi.ResolveAsync("ipfs.io/media", true); + Assert.That(path, Does.StartWith("/ipfs/")); + Assert.That(path, Does.EndWith("/media")); + + path = await ipfs.NameApi.ResolveAsync("/ipfs.io/media", true); + Assert.That(path, Does.StartWith("/ipfs/")); + Assert.That(path, Does.EndWith("/media")); + } + + [Test] + public void Resolve_NoDnsLink() + { + ExceptionAssert.Throws(() => + { + var _ = ipfs.DnsApi.ResolveAsync("google.com").Result; + }); + } + + //[Test] + //[Ignore("Need a working IPNS")] + //public async Task Resolve_DnsLink_Recursive() + //{ + // var ipfs = TestFixture.Ipfs; + + // var media = await ipfs.Generic.ResolveAsync("/ipns/ipfs.io/media"); + // var actual = await ipfs.Generic.ResolveAsync("/ipns/ipfs.io/media", recursive: true); + // Assert.AreNotEqual(media, actual); + //} + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/ObjectApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/ObjectApiTest.cs new file mode 100644 index 0000000000..bdffedc40c --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/ObjectApiTest.cs @@ -0,0 +1,217 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Lib.Dag; +using Catalyst.TestUtils; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + public class ObjectApiTest + { + private IDfsService ipfs; + + public ObjectApiTest() + { + ipfs = TestDfs.GetTestDfs(null, "sha2-256"); + } + + [Test] + public async Task New_Template_Null() + { + var node = await ipfs.ObjectApi.NewAsync(); + Assert.AreEqual("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", node.Id.ToString()); + } + + [Test] + public async Task New_Template_UnixfsDir() + { + var node = await ipfs.ObjectApi.NewAsync("unixfs-dir"); + Assert.AreEqual("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn", node.Id.ToString()); + + node = await ipfs.ObjectApi.NewDirectoryAsync(); + Assert.AreEqual("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn", node.Id.ToString()); + } + + [Test] + public void New_Template_Unknown() + { + ExceptionAssert.Throws(() => + { + ipfs.ObjectApi.NewAsync("unknown-template").GetAwaiter().GetResult(); + }); + } + + [Test] + public async Task Put_Get_Dag() + { + var adata = Encoding.UTF8.GetBytes("alpha"); + var bdata = Encoding.UTF8.GetBytes("beta"); + var alpha = new DagNode(adata); + var beta = new DagNode(bdata, new[] {alpha.ToLink()}); + var x = await ipfs.ObjectApi.PutAsync(beta); + var node = await ipfs.ObjectApi.GetAsync(x.Id); + Assert.AreEqual(beta.DataBytes, node.DataBytes); + Assert.AreEqual(beta.Links.Count(), node.Links.Count()); + Assert.AreEqual(beta.Links.First().Id, node.Links.First().Id); + Assert.AreEqual(beta.Links.First().Name, node.Links.First().Name); + Assert.AreEqual(beta.Links.First().Size, node.Links.First().Size); + } + + [Test] + public async Task Put_Get_Data() + { + var adata = Encoding.UTF8.GetBytes("alpha"); + var bdata = Encoding.UTF8.GetBytes("beta"); + var alpha = new DagNode(adata); + var beta = await ipfs.ObjectApi.PutAsync(bdata, new[] {alpha.ToLink()}); + var node = await ipfs.ObjectApi.GetAsync(beta.Id); + Assert.AreEqual(beta.DataBytes, node.DataBytes); + Assert.AreEqual(beta.Links.Count(), node.Links.Count()); + Assert.AreEqual(beta.Links.First().Id, node.Links.First().Id); + Assert.AreEqual(beta.Links.First().Name, node.Links.First().Name); + Assert.AreEqual(beta.Links.First().Size, node.Links.First().Size); + } + + [Test] + public async Task Data() + { + var adata = Encoding.UTF8.GetBytes("alpha"); + var node = await ipfs.ObjectApi.PutAsync(adata); + await using (var stream = await ipfs.ObjectApi.DataAsync(node.Id)) + { + var bdata = new byte[adata.Length]; + stream.Read(bdata, 0, bdata.Length); + Assert.AreEqual(adata, bdata); + } + } + + [Test] + public async Task Links() + { + var adata = Encoding.UTF8.GetBytes("alpha"); + var bdata = Encoding.UTF8.GetBytes("beta"); + var alpha = new DagNode(adata); + var beta = await ipfs.ObjectApi.PutAsync(bdata, new[] {alpha.ToLink()}); + var links = await ipfs.ObjectApi.LinksAsync(beta.Id); + Assert.AreEqual(beta.Links.Count(), links.Count()); + Assert.AreEqual(beta.Links.First().Id, links.First().Id); + Assert.AreEqual(beta.Links.First().Name, links.First().Name); + Assert.AreEqual(beta.Links.First().Size, links.First().Size); + } + + [Test] + public async Task Stat() + { + var data1 = Encoding.UTF8.GetBytes("Some data 1"); + var data2 = Encoding.UTF8.GetBytes("Some data 2"); + var node2 = new DagNode(data2); + var node1 = await ipfs.ObjectApi.PutAsync(data1, + new[] {node2.ToLink("some-link")}); + var info = await ipfs.ObjectApi.StatAsync(node1.Id); + Assert.AreEqual(1, info.LinkCount); + Assert.AreEqual(11, info.DataSize); + Assert.AreEqual(64, info.BlockSize); + Assert.AreEqual(53, info.LinkSize); + Assert.AreEqual(77, info.CumulativeSize); + } + + [Test] + public async Task Get_Nonexistent() + { + var data = Encoding.UTF8.GetBytes("Some data for net-ipfs-engine-test that cannot be found"); + var node = new DagNode(data); + var id = node.Id; + var cs = new CancellationTokenSource(500); + try + { + var _ = await ipfs.ObjectApi.GetAsync(id, cs.Token); + throw new Exception("Did not throw TaskCanceledException"); + } + catch (TaskCanceledException) + { + // ignore + } + } + + [Test] + + /// + public async Task Get_Inlinefile() + { + var original = ipfs.Options.Block.AllowInlineCid; + try + { + ipfs.Options.Block.AllowInlineCid = true; + + var node = await ipfs.UnixFsApi.AddTextAsync("hiya"); + Assert.AreEqual(1, node.Id.Version); + Assert.True(node.Id.Hash.IsIdentityHash); + + var dag = await ipfs.ObjectApi.GetAsync(node.Id); + Assert.AreEqual(12, dag.Size); + } + finally + { + ipfs.Options.Block.AllowInlineCid = original; + } + } + + [Test] + public async Task Links_InlineCid() + { + var original = ipfs.Options.Block.AllowInlineCid; + try + { + ipfs.Options.Block.AllowInlineCid = true; + + var node = await ipfs.UnixFsApi.AddTextAsync("hiya"); + Assert.AreEqual(1, node.Id.Version); + Assert.True(node.Id.Hash.IsIdentityHash); + + var links = await ipfs.ObjectApi.LinksAsync(node.Id); + Assert.AreEqual(0, links.Count()); + } + finally + { + ipfs.Options.Block.AllowInlineCid = original; + } + } + + [Test] + public async Task Links_RawCid() + { + var blob = new byte[2048]; + var cid = await ipfs.BlockApi.PutAsync(blob, "raw"); + + var links = await ipfs.ObjectApi.LinksAsync(cid); + Assert.AreEqual(0, links.Count()); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/ObjectStatTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/ObjectStatTest.cs new file mode 100644 index 0000000000..ccf7a2883d --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/ObjectStatTest.cs @@ -0,0 +1,49 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Dfs.CoreApi; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + public class ObjectStatTest + { + [Test] + public void Properties() + { + var stat = new ObjectStat + { + BlockSize = 1, + CumulativeSize = 2, + DataSize = 3, + LinkCount = 4, + LinkSize = 5 + }; + Assert.AreEqual(1, stat.BlockSize); + Assert.AreEqual(2, stat.CumulativeSize); + Assert.AreEqual(3, stat.DataSize); + Assert.AreEqual(4, stat.LinkCount); + Assert.AreEqual(5, stat.LinkSize); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/PinApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/PinApiTest.cs new file mode 100644 index 0000000000..063b222f74 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/PinApiTest.cs @@ -0,0 +1,138 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.Dag; +using Catalyst.TestUtils; +using Lib.P2P; +using MultiFormats; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + public class PinApiTest + { + private readonly IDfsService _dfs; + + public PinApiTest() + { + _dfs = TestDfs.GetTestDfs(); + } + + [Test] + public async Task Add_Remove() + { + var result = await _dfs.UnixFsApi.AddTextAsync("I am pinned"); + var id = result.Id; + + var pins = await _dfs.PinApi.AddAsync(id); + Assert.True(pins.Any(pin => pin == id)); + var all = await _dfs.PinApi.ListAsync(); + Assert.True(all.Any(pin => pin == id)); + + pins = await _dfs.PinApi.RemoveAsync(id); + Assert.True(pins.Any(pin => pin == id)); + all = await _dfs.PinApi.ListAsync(); + Assert.False(all.Any(pin => pin == id)); + } + + [Test] + public async Task Remove_Unknown() + { + var dag = new DagNode(Encoding.UTF8.GetBytes("some unknown info for net-ipfs-engine-pin-test")); + await _dfs.PinApi.RemoveAsync(dag.Id); + } + + [Test] + public async Task Inline_Cid() + { + var cid = new Cid + { + ContentType = "raw", + Hash = MultiHash.ComputeHash(new byte[] + { + 1, 2, 3 + }, "identity") + }; + var pins = await _dfs.PinApi.AddAsync(cid, false); + Assert.Contains(cid, pins.ToArray()); + var all = await _dfs.PinApi.ListAsync(); + Assert.Contains(cid, all.ToArray()); + + var removals = await _dfs.PinApi.RemoveAsync(cid, false); + Assert.Contains(cid, removals.ToArray()); + all = await _dfs.PinApi.ListAsync(); + Assert.That(all.ToArray(), Does.Not.Contain(cid)); + } + + [Test] + public void Add_Unknown() + { + var dag = new DagNode(Encoding.UTF8.GetBytes("some unknown info for net-ipfs-engine-pin-test")); + ExceptionAssert.Throws(() => + { + var cts = new CancellationTokenSource(250); + var _ = _dfs.PinApi.AddAsync(dag.Id, true, cts.Token).Result; + }); + } + + [Test] + public async Task Add_Recursive() + { + var options = new AddFileOptions + { + ChunkSize = 3, + Pin = false, + RawLeaves = true, + Wrap = true, + }; + var node = await _dfs.UnixFsApi.AddTextAsync("hello world", options); + var cids = await _dfs.PinApi.AddAsync(node.Id); + Assert.AreEqual(6, cids.Count()); + } + + [Test] + public async Task Remove_Recursive() + { + var options = new AddFileOptions + { + ChunkSize = 3, + Pin = false, + RawLeaves = true, + Wrap = true, + }; + var node = await _dfs.UnixFsApi.AddTextAsync("hello world", options); + var cids = await _dfs.PinApi.AddAsync(node.Id); + Assert.AreEqual(6, cids.Count()); + + var removedCids = await _dfs.PinApi.RemoveAsync(node.Id); + Assert.AreEqual(cids.ToArray(), removedCids.ToArray()); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/PingResultTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/PingResultTest.cs new file mode 100644 index 0000000000..dc4bb9fec7 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/PingResultTest.cs @@ -0,0 +1,47 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Lib.P2P.Protocols; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + public class PingResultTest + { + [Test] + public void Properties() + { + var time = TimeSpan.FromSeconds(3); + var r = new PingResult + { + Success = true, + Text = "ping", + Time = time + }; + Assert.AreEqual(true, r.Success); + Assert.AreEqual("ping", r.Text); + Assert.AreEqual(time, r.Time); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/PubSubApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/PubSubApiTest.cs new file mode 100644 index 0000000000..38b39931bd --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/PubSubApiTest.cs @@ -0,0 +1,236 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.TestUtils; +using FluentAssertions; +using Lib.P2P.PubSub; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + public class PubSubApiTest + { + private IDfsService ipfs; + + public PubSubApiTest() + { + ipfs = TestDfs.GetTestDfs(); + } + + [Test] + public void Api_Exists() + { + Assert.NotNull(ipfs.PubSubApi); + } + + [Test] + public void Peers_Unknown_Topic() + { + var topic = "net-ipfs-http-client-test-unknown" + Guid.NewGuid(); + var peers = ipfs.PubSubApi.PeersAsync(topic).Result.ToArray(); + Assert.AreEqual(0, peers.Length); + } + + [Test] + public async Task Subscribed_Topics() + { + var topic = Guid.NewGuid().ToString(); + var cs = new CancellationTokenSource(); + await ipfs.StartAsync(); + try + { + await ipfs.PubSubApi.SubscribeAsync(topic, msg => { }, cs.Token); + var topics = ipfs.PubSubApi.SubscribedTopicsAsync(cs.Token).Result.ToArray(); + Assert.True(topics.Length > 0); + topics.Should().Contain(topic); + } + finally + { + await ipfs.StopAsync(); + cs.Cancel(); + } + } + + private volatile int _messageCount; + + [Test] + public async Task Subscribe() + { + _messageCount = 0; + var topic = Guid.NewGuid().ToString(); + var cs = new CancellationTokenSource(); + await ipfs.StartAsync(); + try + { + await ipfs.PubSubApi.SubscribeAsync(topic, msg => { Interlocked.Increment(ref _messageCount); }, cs.Token); + await ipfs.PubSubApi.PublishAsync(topic, "hello world!", cs.Token); + + await Task.Delay(100, cs.Token); + Assert.AreEqual(1, _messageCount); + } + finally + { + await ipfs.StopAsync(); + cs.Cancel(); + } + } + + [Test] + public async Task Subscribe_Mutiple_Messages() + { + _messageCount = 0; + var messages = "hello world this is pubsub".Split(); + var topic = Guid.NewGuid().ToString(); + var cs = new CancellationTokenSource(); + await ipfs.StartAsync(); + try + { + await ipfs.PubSubApi.SubscribeAsync(topic, msg => { Interlocked.Increment(ref _messageCount); }, cs.Token); + foreach (var msg in messages) + { + await ipfs.PubSubApi.PublishAsync(topic, msg, cs.Token); + } + + await Task.Delay(100, cs.Token); + Assert.AreEqual(messages.Length, _messageCount); + } + finally + { + await ipfs.StopAsync(); + cs.Cancel(); + } + } + + [Test] + public async Task Multiple_Subscribe_Mutiple_Messages() + { + _messageCount = 0; + var messages = "hello world this is pubsub".Split(); + var topic = Guid.NewGuid().ToString(); + var cs = new CancellationTokenSource(); + + void ProcessMessage(IPublishedMessage msg) { Interlocked.Increment(ref _messageCount); } + await ipfs.StartAsync(); + try + { + await ipfs.PubSubApi.SubscribeAsync(topic, ProcessMessage, cs.Token); + await ipfs.PubSubApi.SubscribeAsync(topic, ProcessMessage, cs.Token); + foreach (var msg in messages) + { + await ipfs.PubSubApi.PublishAsync(topic, msg, cs.Token); + } + + await Task.Delay(100, cs.Token); + Assert.AreEqual(messages.Length * 2, _messageCount); + } + finally + { + await ipfs.StopAsync(); + cs.Cancel(); + } + } + + private volatile int _messageCount1; + + [Test] + public async Task Unsubscribe() + { + _messageCount1 = 0; + var topic = Guid.NewGuid().ToString(); + var cs = new CancellationTokenSource(); + await ipfs.StartAsync(); + try + { + await ipfs.PubSubApi.SubscribeAsync(topic, msg => { Interlocked.Increment(ref _messageCount1); }, cs.Token); + await ipfs.PubSubApi.PublishAsync(topic, "hello world!", default); + await Task.Delay(100, default); + Assert.AreEqual(1, _messageCount1); + + cs.Cancel(); + await ipfs.PubSubApi.PublishAsync(topic, "hello world!!!", default); + await Task.Delay(100, default); + Assert.AreEqual(1, _messageCount1); + } + finally + { + await ipfs.StopAsync(); + } + } + + [Test] + public async Task Subscribe_BinaryMessage() + { + var messages = new List(); + var expected = new byte[] {0, 1, 2, 4, (byte) 'a', (byte) 'b', 0xfe, 0xff}; + var topic = Guid.NewGuid().ToString(); + var cs = new CancellationTokenSource(); + await ipfs.StartAsync(); + try + { + await ipfs.PubSubApi.SubscribeAsync(topic, msg => { messages.Add(msg); }, cs.Token); + await ipfs.PubSubApi.PublishAsync(topic, expected, cs.Token); + + await Task.Delay(100, cs.Token); + Assert.AreEqual(1, messages.Count); + Assert.AreEqual(expected, messages[0].DataBytes); + } + finally + { + await ipfs.StopAsync(); + cs.Cancel(); + } + } + + [Test] + public async Task Subscribe_StreamMessage() + { + var messages = new List(); + var expected = new byte[] {0, 1, 2, 4, (byte) 'a', (byte) 'b', 0xfe, 0xff}; + var topic = Guid.NewGuid().ToString(); + var cs = new CancellationTokenSource(); + await ipfs.StartAsync(); + try + { + await ipfs.PubSubApi.SubscribeAsync(topic, msg => { messages.Add(msg); }, cs.Token); + var ms = new MemoryStream(expected, false); + await ipfs.PubSubApi.PublishAsync(topic, ms, cs.Token); + + await Task.Delay(100, cs.Token); + Assert.AreEqual(1, messages.Count); + Assert.AreEqual(expected, messages[0].DataBytes); + } + finally + { + cs.Cancel(); + await ipfs.StopAsync(); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/StatsApiTests.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/StatsApiTests.cs new file mode 100644 index 0000000000..fabfc212d2 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/StatsApiTests.cs @@ -0,0 +1,53 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.TestUtils; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class StatsApiTest + { + private readonly IDfsService _dfs; + + public StatsApiTest() + { + _dfs = TestDfs.GetTestDfs(); + } + + [Test] + public void Exists() { Assert.NotNull(_dfs.StatsApi); } + + [Test] + public async Task SmokeTest() + { + await _dfs.StatsApi.GetBandwidthStatsAsync(); + _dfs.StatsApi.GetBitSwapStats(); + await _dfs.StatsApi.GetRepositoryStatsAsync(); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/SwarmApiTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/SwarmApiTest.cs new file mode 100644 index 0000000000..665bae4bd3 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/CoreApi/SwarmApiTest.cs @@ -0,0 +1,200 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +// using System.IO; +// using System.Linq; +// using System.Threading.Tasks; +// using Catalyst.Abstractions.Dfs; +// using Catalyst.Core.Lib.Cryptography; +// using Catalyst.Core.Modules.Dfs.Tests.Utils; +// using Catalyst.Core.Modules.Hashing; +// using Catalyst.TestUtils; +// using Lib.P2P.Cryptography; +// using MultiFormats; +// using MultiFormats.Registry; +// using Newtonsoft.Json.Linq; +// using NUnit.Framework; +// +// +// namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.CoreApi +// { +// public class SwarmApiTest +// { +// private IDfsService ipfs; +// +// public SwarmApiTest(TestContext output) +// { +// ipfs = new TestFixture(output).Ipfs; +// } +// +// readonly MultiAddress somewhere = "/ip4/127.0.0.1"; +// +// [Test] +// public async Task Filter_Add_Remove() +// { +// var addr = await ipfs.Swarm.AddAddressFilterAsync(somewhere); +// Assert.NotNull(addr); +// Assert.AreEqual(somewhere, addr); +// var addrs = await ipfs.Swarm.ListAddressFiltersAsync(); +// Assert.True(addrs.Any(a => a == somewhere)); +// +// addr = await ipfs.Swarm.RemoveAddressFilterAsync(somewhere); +// Assert.NotNull(addr); +// Assert.AreEqual(somewhere, addr); +// addrs = await ipfs.Swarm.ListAddressFiltersAsync(); +// Assert.False(addrs.Any(a => a == somewhere)); +// } +// +// // [Test] +// // [Ignore("https://github.com/richardschneider/net-ipfs-engine/issues/74")] +// // public async Task Connect_Disconnect_Mars() +// // { +// // var mars = "/dns/mars.i.ipfs.io/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"; +// // await ipfs.StartAsync(); +// // try +// // { +// // await ipfs.Swarm.ConnectAsync(mars); +// // var peers = await ipfs.Swarm.PeersAsync(); +// // Assert.True(peers.Any(p => p.Id == "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ")); +// // await ipfs.Swarm.DisconnectAsync(mars); +// // } +// // finally +// // { +// // await ipfs.StopAsync(); +// // } +// // } +// +// // [Test] +// // [Ignore("TODO: Move to interop tests")] +// // public async Task JsIPFS_Connect() +// // { +// // var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); +// // var remoteId = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb"; +// // var remoteAddress = $"/ip4/127.0.0.1/tcp/4002/ipfs/{remoteId}"; +// // +// // Assert.AreEqual(0, (await ipfs.Swarm.PeersAsync()).Count()); +// // await ipfs.Swarm.ConnectAsync(remoteAddress, cts.Token); +// // Assert.AreEqual(1, (await ipfs.Swarm.PeersAsync()).Count()); +// // +// // await ipfs.Swarm.DisconnectAsync(remoteAddress); +// // Assert.AreEqual(0, (await ipfs.Swarm.PeersAsync()).Count()); +// // } +// // +// // [Test] +// // [Ignore("TODO: Move to interop tests")] +// // public async Task GoIPFS_Connect() +// // { +// // var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); +// // var remoteId = "QmdoxrwszT6b9srLXHYBPFVRXmZSFAosWLXoQS9TEEAaix"; +// // var remoteAddress = $"/ip4/127.0.0.1/tcp/4001/ipfs/{remoteId}"; +// // +// // Assert.AreEqual(0, (await ipfs.Swarm.PeersAsync()).Count()); +// // await ipfs.Swarm.ConnectAsync(remoteAddress, cts.Token); +// // Assert.AreEqual(1, (await ipfs.Swarm.PeersAsync()).Count()); +// // +// // await ipfs.Swarm.DisconnectAsync(remoteAddress); +// // Assert.AreEqual(0, (await ipfs.Swarm.PeersAsync()).Count()); +// // } +// // +// // [Test] +// // [Ignore("TODO: Move to interop tests")] +// // public async Task GoIPFS_Connect_v0_4_17() +// // { +// // var cts = new CancellationTokenSource(TimeSpan.FromSeconds(10)); +// // var remoteId = "QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd"; +// // var remoteAddress = $"/ip4/178.62.158.247/tcp/4001/ipfs/{remoteId}"; +// // +// // Assert.AreEqual(0, (await ipfs.Swarm.PeersAsync()).Count()); +// // await ipfs.Swarm.ConnectAsync(remoteAddress, cts.Token); +// // Assert.AreEqual(1, (await ipfs.Swarm.PeersAsync()).Count()); +// // +// // await ipfs.Swarm.DisconnectAsync(remoteAddress); +// // Assert.AreEqual(0, (await ipfs.Swarm.PeersAsync()).Count()); +// // } +// +// [Test] +// public async Task PrivateNetwork_WithOptionsKey() +// { +// using (var ipfs = CreateNode()) +// { +// try +// { +// ipfs.Options.Swarm.PrivateNetworkKey = new PreSharedKey().Generate(); +// var swarm = await ipfs.SwarmService; +// Assert.NotNull(swarm.NetworkProtector); +// } +// finally +// { +// if (Directory.Exists(ipfs.Options.Repository.Folder)) +// { +// Directory.Delete(ipfs.Options.Repository.Folder, true); +// } +// } +// } +// } +// +// [Test] +// public async Task PrivateNetwork_WithSwarmKeyFile() +// { +// using (var ipfs = CreateNode()) +// { +// try +// { +// var key = new PreSharedKey().Generate(); +// var path = Path.Combine(ipfs.Options.Repository.ExistingFolder(), "swarm.key"); +// using (var x = File.CreateText(path)) +// { +// key.Export(x); +// } +// +// var swarm = await ipfs.SwarmService; +// Assert.NotNull(swarm.NetworkProtector); +// } +// finally +// { +// if (Directory.Exists(ipfs.Options.Repository.Folder)) +// { +// Directory.Delete(ipfs.Options.Repository.Folder, true); +// } +// } +// } +// } +// +// static int nodeNumber = 0; +// +// DfsService CreateNode() +// { +// var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); +// var testPasswordManager = new PasswordManager(new TestPasswordReader(), new PasswordRegistry()); +// var ipfs = new DfsService(hashProvider, testPasswordManager); +// ipfs.Options.Repository.Folder = Path.Combine(Path.GetTempPath(), $"swarm-{nodeNumber++}"); +// ipfs.Options.KeyChain.DefaultKeySize = 512; +// ipfs.Config.SetAsync( +// "Addresses.Swarm", +// JToken.FromObject(new string[] {"/ip4/0.0.0.0/tcp/4007"}) +// ).Wait(); +// +// return ipfs; +// } +// } +// } diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DagLinkTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DagLinkTest.cs new file mode 100644 index 0000000000..a5be7b22f2 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DagLinkTest.cs @@ -0,0 +1,103 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using Catalyst.Core.Lib.Dag; +using Catalyst.TestUtils; +using Google.Protobuf; +using MultiFormats; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class DagLinkTest + { + [Test] + public void Creating() + { + var link = new DagLink("abc", "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", 5); + Assert.AreEqual("abc", link.Name); + Assert.AreEqual("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", link.Id.ToString()); + Assert.AreEqual(5, link.Size); + } + + [Test] + public void Cloning() + { + var link = new DagLink("abc", "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", 5); + var clone = new DagLink(link); + + Assert.AreEqual("abc", clone.Name); + Assert.AreEqual("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", clone.Id.ToString()); + Assert.AreEqual(5, clone.Size); + } + + [Test] + public void Encoding() + { + const string encoded = "0a22122023dca2a7429612378554b0bb5b85012dec00a17cc2c673f17d2b76a50b839cd51201611803"; + var link = new DagLink("a", "QmQke7LGtfu3GjFP3AnrP8vpEepQ6C5aJSALKAq653bkRi", 3); + link.ToArray(); + Assert.AreEqual(encoded, link.ToArray().ToHexString()); + } + + [Test] + public void Encoding_EmptyName() + { + var encoded = "0a22122023dca2a7429612378554b0bb5b85012dec00a17cc2c673f17d2b76a50b839cd512001803"; + var link = new DagLink("", "QmQke7LGtfu3GjFP3AnrP8vpEepQ6C5aJSALKAq653bkRi", 3); + link.ToArray(); + Assert.AreEqual(encoded, link.ToArray().ToHexString()); + } + + [Test] + public void Encoding_NullName() + { + const string encoded = "0a22122023dca2a7429612378554b0bb5b85012dec00a17cc2c673f17d2b76a50b839cd51803"; + var link = new DagLink(null, "QmQke7LGtfu3GjFP3AnrP8vpEepQ6C5aJSALKAq653bkRi", 3); + link.ToArray(); + Assert.AreEqual(encoded, link.ToArray().ToHexString()); + } + + [Test] + public void Null_Stream() + { + TestUtils.ExceptionAssert.Throws(() => new DagLink((CodedInputStream) null)); + TestUtils.ExceptionAssert.Throws(() => new DagLink((Stream) null)); + } + + [Test] + public void Cid_V1() + { + var link = new DagLink("hello", + "zB7NCdng5WffuNCgHu4PhDj7nbtuVrhPc2pMhanNxYKRsECdjX9nd44g6CRu2xNrj2bG2NNaTsveL5zDGWhbfiug3VekW", 11); + Assert.AreEqual("hello", link.Name); + Assert.AreEqual(1, link.Id.Version); + Assert.AreEqual("raw", link.Id.ContentType); + Assert.AreEqual("sha2-512", link.Id.Hash.Algorithm.Name); + Assert.AreEqual(11, link.Size); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DagNodeTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DagNodeTest.cs new file mode 100644 index 0000000000..5f2c6650f5 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DagNodeTest.cs @@ -0,0 +1,312 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Linq; +using System.Text; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Lib.Dag; +using Catalyst.Core.Modules.Hashing; +using Catalyst.TestUtils; +using Google.Protobuf; +using MultiFormats; +using MultiFormats.Registry; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class DagNodeTest + { + public DagNodeTest() + { + new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("sha2-256")); + } + + [Test] + public void EmptyDag() + { + var node = new DagNode((byte[]) null); + Assert.AreEqual(0, node.DataBytes.Length); + Assert.AreEqual(0, node.Links.Count()); + Assert.AreEqual(0, node.Size); + Assert.AreEqual("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", node.Id.ToString()); + + RoundtripTest(node); + } + + [Test] + public void DataOnlyDag() + { + var abc = Encoding.UTF8.GetBytes("abc"); + var node = new DagNode(abc); + + Assert.AreEqual(abc, node.DataBytes); + Assert.AreEqual(0, node.Links.Count()); + Assert.AreEqual("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", node.Id.ToString()); + Assert.AreEqual(5, node.Size); + + RoundtripTest(node); + } + + [Test] + public void LinkOnlyDag() + { + var a = Encoding.UTF8.GetBytes("a"); + var anode = new DagNode(a); + var alink = anode.ToLink("a"); + + var node = new DagNode(null, new[] + { + alink + }); + Assert.AreEqual(0, node.DataBytes.Length); + Assert.AreEqual(1, node.Links.Count()); + Assert.AreEqual("QmVdMJFGTqF2ghySAmivGiQvsr9ZH7ujnNGBkLNNCe4HUE", node.Id.ToString()); + Assert.AreEqual(43, node.Size); + + RoundtripTest(node); + } + + [Test] + public void MultipleLinksOnlyDag() + { + var a = Encoding.UTF8.GetBytes("a"); + var anode = new DagNode(a); + var alink = anode.ToLink("a"); + + var b = Encoding.UTF8.GetBytes("b"); + var bnode = new DagNode(b); + var blink = bnode.ToLink("b"); + + var node = new DagNode(null, new[] + { + alink, blink + }); + Assert.AreEqual(0, node.DataBytes.Length); + Assert.AreEqual(2, node.Links.Count()); + Assert.AreEqual("QmbNgNPPykP4YTuAeSa3DsnBJWLVxccrqLUZDPNQfizGKs", node.Id.ToString()); + + RoundtripTest(node); + } + + [Test] + public void MultipleLinksDataDag() + { + var a = Encoding.UTF8.GetBytes("a"); + var anode = new DagNode(a); + var alink = anode.ToLink("a"); + + var b = Encoding.UTF8.GetBytes("b"); + var bnode = new DagNode(b); + var blink = bnode.ToLink("b"); + + var ab = Encoding.UTF8.GetBytes("ab"); + var node = new DagNode(ab, new[] + { + alink, blink + }); + + Assert.AreEqual(ab, node.DataBytes); + Assert.AreEqual(2, node.Links.Count()); + Assert.AreEqual("Qma5sYpEc9hSYdkuXpMDJYem95Mj7hbEd9C412dEQ4ZkfP", node.Id.ToString()); + + RoundtripTest(node); + } + + [Test] + public void LinksAreSorted() + { + var a = Encoding.UTF8.GetBytes("a"); + var anode = new DagNode(a); + var alink = anode.ToLink("a"); + + var b = Encoding.UTF8.GetBytes("b"); + var bnode = new DagNode(b); + var blink = bnode.ToLink("b"); + + var ab = Encoding.UTF8.GetBytes("ab"); + var node = new DagNode(ab, new[] + { + blink, alink + }); + Assert.AreEqual(ab, node.DataBytes); + Assert.AreEqual(2, node.Links.Count()); + Assert.AreEqual("Qma5sYpEc9hSYdkuXpMDJYem95Mj7hbEd9C412dEQ4ZkfP", node.Id.ToString()); + } + + [Test] + public void HashingAlgorithmTest() + { + var abc = Encoding.UTF8.GetBytes("abc"); + var node = new DagNode(abc); + Assert.AreEqual(abc, node.DataBytes); + Assert.AreEqual(0, node.Links.Count()); + Assert.AreEqual("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", node.Id.ToString()); + Assert.AreEqual(5, node.Size); + + RoundtripTest(node); + } + + [Test] + public void ToLink() + { + var abc = Encoding.UTF8.GetBytes("abc"); + var node = new DagNode(abc); + var link = node.ToLink(); + Assert.AreEqual("", link.Name); + Assert.AreEqual(node.Id, link.Id); + Assert.AreEqual(node.Size, link.Size); + } + + [Test] + public void ToLinkWithName() + { + var abc = Encoding.UTF8.GetBytes("abc"); + var node = new DagNode(abc); + var link = node.ToLink("abc"); + Assert.AreEqual("abc", link.Name); + Assert.AreEqual(node.Id.ToString(), link.Id.ToString()); + Assert.AreEqual(node.Size, link.Size); + } + + [Test] + public void AddLink() + { + var a = Encoding.UTF8.GetBytes("a"); + var anode = new DagNode(a); + + var b = Encoding.UTF8.GetBytes("b"); + var bnode = new DagNode(b); + + var cnode = bnode.AddLink(anode.ToLink()); + Assert.False(ReferenceEquals(bnode, cnode)); + Assert.AreEqual(1, cnode.DataBytes.Length); + Assert.AreEqual(1, cnode.Links.Count()); + Assert.AreEqual(anode.Id.ToString(), cnode.Links.First().Id.ToString()); + Assert.AreEqual(anode.Size, cnode.Links.First().Size); + + RoundtripTest(cnode); + } + + [Test] + public void RemoveLink() + { + var a = Encoding.UTF8.GetBytes("a"); + var anode = new DagNode(a); + + var b = Encoding.UTF8.GetBytes("b"); + var bnode = new DagNode(b); + + var c = Encoding.UTF8.GetBytes("c"); + var cnode = new DagNode(c, new[] + { + anode.ToLink(), bnode.ToLink() + }); + + var dnode = cnode.RemoveLink(anode.ToLink()); + Assert.False(ReferenceEquals(dnode, cnode)); + Assert.AreEqual(1, dnode.DataBytes.Length); + Assert.AreEqual(1, dnode.Links.Count()); + Assert.AreEqual(bnode.Id.ToString(), dnode.Links.First().Id.ToString()); + Assert.AreEqual(bnode.Size, dnode.Links.First().Size); + + RoundtripTest(cnode); + } + + [Test] + public void NullStream() + { + TestUtils.ExceptionAssert.Throws(() => new DagNode((CodedInputStream) null)); + TestUtils.ExceptionAssert.Throws(() => new DagNode((Stream) null)); + } + + [Test] + public void LinkWithCidv1() + { + var data = + "124F0A4401551340309ECC489C12D6EB4CC40F50C902F2B4D0ED77EE511A7C7A9BCD3CA86D4CD86F989DD35BC5FF499670DA34255B45B0CFD830E81F605DCF7DC5542E93AE9CD76F120568656C6C6F180B0A020801" + .ToHexBuffer(); + var ms = new MemoryStream(data, false); + var node = new DagNode(ms); + Assert.AreEqual("0801", node.DataBytes.ToHexString()); + Assert.AreEqual(1, node.Links.Count()); + var link = node.Links.First(); + Assert.AreEqual("hello", link.Name); + Assert.AreEqual(1, link.Id.Version); + Assert.AreEqual("raw", link.Id.ContentType); + Assert.AreEqual("sha2-512", link.Id.Hash.Algorithm.Name); + Assert.AreEqual(11, link.Size); + } + + [Test] + public void SettingId() + { + var a = new DagNode((byte[]) null); + var b = new DagNode((byte[]) null) + { + // Wrong hash but allowed. + Id = "QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1m" + }; + Assert.AreEqual(a.DataBytes.Length, b.DataBytes.Length); + Assert.AreEqual(a.Links.Count(), b.Links.Count()); + Assert.AreEqual(a.Size, b.Size); + Assert.AreNotEqual(a.Id, b.Id); + + RoundtripTest(b); + } + + private static void RoundtripTest(IDagNode a) + { + var ms = new MemoryStream(); + a.Write(ms); + ms.Position = 0; + var b = new DagNode(ms); + + Assert.AreEqual(a.DataBytes, b.DataBytes); + Assert.AreEqual(a.ToArray(), b.ToArray()); + Assert.AreEqual(a.Links.Count(), b.Links.Count()); + foreach (var _ in a.Links.Zip(b.Links, (first, second) => + { + Assert.AreEqual(first.Id, second.Id); + Assert.AreEqual(first.Name, second.Name); + Assert.AreEqual(first.Size, second.Size); + return first; + }).ToArray()) + + using (var first = a.DataStream) + { + using (var second = b.DataStream) + { + Assert.AreEqual(first.Length, second.Length); + for (var i = 0; i < first.Length; ++i) + { + Assert.AreEqual(first.ReadByte(), second.ReadByte()); + } + } + } + } + } +} + diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DevDfsTests.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DevDfsTests.cs deleted file mode 100644 index e317320800..0000000000 --- a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DevDfsTests.cs +++ /dev/null @@ -1,79 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.Threading; -using System.Threading.Tasks; -using Catalyst.Abstractions.Dfs; -using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Modules.Hashing; -using Catalyst.TestUtils; -using FluentAssertions; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using Xunit.Abstractions; - -namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests -{ - public sealed class DevDfsTests : FileSystemBasedTest - { - private readonly IDfs _dfs; - private readonly CancellationToken _cancellationToken; - - public DevDfsTests(ITestOutputHelper output) : base(output) - { - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - _cancellationToken = new CancellationTokenSource(TimeSpan.FromSeconds(200)).Token; - _dfs = new DevDfs(FileSystem, hashProvider); - } - - [Fact(Skip = "https://github.com/catalyst-network/Catalyst.Node/issues/986")] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public async Task AddTextAsync_Can_Be_Retrieved_With_ReadTextAsync() - { - var content = "Lorem Ipsum or something"; - var fileName = await _dfs.AddTextAsync(content, _cancellationToken); - - Thread.Sleep(100); - - var retrievedContent = await _dfs.ReadTextAsync(fileName, _cancellationToken); - - retrievedContent.Should().Be(content); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public async Task AddAsync_Can_Be_Retrieved_With_ReadAsync() - { - var content = BitConverter.GetBytes(123456); - var fileName = await _dfs.AddAsync(content.ToMemoryStream(), cancellationToken: _cancellationToken); - - Thread.Sleep(100); - - var retrievedContent = await _dfs.ReadAsync(fileName, _cancellationToken); - var fileContent = await retrievedContent.ReadAllBytesAsync(_cancellationToken); - - fileContent.Should().BeEquivalentTo(content); - } - } -} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DfsHttpTests.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DfsHttpTests.cs index abf8044ba6..18c47f3eaf 100644 --- a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DfsHttpTests.cs +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DfsHttpTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,9 +30,9 @@ // private readonly Dfs _dfs; // private readonly DfsGateway _dfsGateway; -// public DfsHttpTests(ITestOutputHelper output) : base(output) +// public DfsHttpTests() : base(TestContext.CurrentContext) // { -// var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); +// var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); // var passwordReader = Substitute.For(); // passwordReader.RetrieveOrPromptAndAddPasswordToRegistry(Arg.Any(), Arg.Any()).ReturnsForAnyArgs(TestPasswordReader.BuildSecureStringPassword("abcd")); // var logger = Substitute.For(); @@ -42,9 +42,9 @@ // } // [Theory] -// [Trait(Traits.TestType, Traits.IntegrationTest)] -// [InlineData("Expected content str")] -// [InlineData("Expected @!�:!$!%(")] +// +// [TestCase("Expected content str")] +// [TestCase("Expected @!�:!$!%(")] // public async Task Should_have_a_URL_for_content(string expectedText) // { // var id = await _dfs.AddTextAsync(expectedText).ConfigureAwait(false); @@ -53,9 +53,9 @@ // } // [Theory] -// [Trait(Traits.TestType, Traits.IntegrationTest)] -// [InlineData("Expected content str")] -// [InlineData("Expected @!�:!$!%(")] +// +// [TestCase("Expected content str")] +// [TestCase("Expected @!�:!$!%(")] // public async Task Should_serve_the_content(string expectedText) // { // var id = await _dfs.AddTextAsync(expectedText); diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DfsServiceTests.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DfsServiceTests.cs new file mode 100644 index 0000000000..5d3bf64db1 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DfsServiceTests.cs @@ -0,0 +1,302 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Autofac; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Types; +using Catalyst.TestUtils; +using FluentAssertions; +using Lib.P2P; +using Lib.P2P.Cryptography; +using MultiFormats; +using Newtonsoft.Json.Linq; +using NSubstitute; +using NUnit.Framework; +using Serilog; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class DfsServiceTests : FileSystemBasedTest + { + private IDfsService _dfs1; + private IDfsService _dfs2; + + [SetUp] + public void Init() + { + this.Setup(TestContext.CurrentContext); + ContainerProvider.Container.Resolve(); + + var passwordReader = Substitute.For(); + passwordReader.RetrieveOrPromptAndAddPasswordToRegistry(Arg.Any(), Arg.Any()) + .Returns(TestPasswordReader.BuildSecureStringPassword("abcd")); + + Substitute.For(); + + _dfs1 = TestDfs.GetTestDfs(); + _dfs2 = TestDfs.GetTestDfs(); + } + + [Test] + public async Task DFS_should_add_and_read_text() + { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + + const string text = "good morning"; + + var id = await _dfs1.UnixFsApi.AddTextAsync(text, cancel: cts.Token); + var content = await _dfs1.UnixFsApi?.ReadAllTextAsync(id.Id, cts.Token); + + content.Should().Be(text); + } + + [Test] + public async Task DFS_should_add_and_read_binary() + { + var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); + var binary = new byte[] + { + 1, 2, 3 + }; + var ms = new MemoryStream(binary); + + var id = await _dfs1.UnixFsApi.AddAsync(ms, "", cancel: cts.Token); + await using (var stream = await _dfs1.UnixFsApi.ReadFileAsync(id.Id, cts.Token)) + { + var content = new byte[binary.Length]; + await stream.ReadAsync(content, 0, content.Length, cts.Token); + content.Should().Equal(binary); + } + } + + [Ignore("waiting for Dns seed fix: https://github.com/catalyst-network/Catalyst.Framework/issues/1075")] + public async Task DFS_should_connect_to_a_seednode() + { + var seeds = (await _dfs1.BootstrapApi.ListAsync().ConfigureAwait(false)) + .Select(a => a.PeerId) + .ToArray(); + Assert.True(seeds.Length > 0, "no seed nodes defined"); + + // Wait for a connection to a seed node. + var start = DateTime.Now; + var end = DateTime.Now.AddSeconds(15); + var found = false; + while (!found) + { + Assert.True(DateTime.Now <= end, "timeout"); + var peers = await _dfs1.SwarmApi.PeersAsync().ConfigureAwait(false); + found = peers.Any(p => seeds.Contains(p.Id)); + await Task.Delay(100).ConfigureAwait(false); + } + + TestContext.WriteLine( + $"Found in {(DateTime.Now - start).TotalSeconds.ToString(CultureInfo.InvariantCulture)} seconds."); + } + + [Test] + public async Task Can_Dispose() + { + using var node = TestDfs.GetTestDfs(); + await node.StartAsync(); + } + + [Test] + public async Task Can_Start_And_Stop() + { + var peer = _dfs1.LocalPeer; + + Assert.False(_dfs1.IsStarted); + await _dfs1.StartAsync(); + Assert.True(_dfs1.IsStarted); + Assert.AreNotEqual(0, peer.Addresses.Count()); + await _dfs1.StopAsync(); + Assert.False(_dfs1.IsStarted); + Assert.AreEqual(0, peer.Addresses.Count()); + + await _dfs1.StartAsync(); + Assert.AreNotEqual(0, peer.Addresses.Count()); + await _dfs1.StopAsync(); + Assert.AreEqual(0, peer.Addresses.Count()); + + await _dfs1.StartAsync(); + Assert.AreNotEqual(0, peer.Addresses.Count()); + ExceptionAssert.Throws(() => _dfs1.StartAsync().Wait()); + await _dfs1.StopAsync(); + Assert.AreEqual(0, peer.Addresses.Count()); + } + + [Test] + public async Task Can_Start_And_Stop_MultipleEngines() + { + var peer1 = _dfs1.LocalPeer; + var peer2 = _dfs2.LocalPeer; + + for (int n = 0; n < 3; ++n) + { + await _dfs1.StartAsync(); + Assert.AreNotEqual(0, peer1.Addresses.Count()); + await _dfs2.StartAsync(); + Assert.AreNotEqual(0, peer2.Addresses.Count()); + + await _dfs2.StopAsync(); + Assert.AreEqual(0, peer2.Addresses.Count()); + await _dfs1.StopAsync(); + Assert.AreEqual(0, peer1.Addresses.Count()); + } + } + + [Test] + public async Task Can_Use_Private_Node() + { + using (var ipfs = TestDfs.GetTestDfs()) + { + ipfs.Options.Discovery.BootstrapPeers = new MultiAddress[0]; + ipfs.Options.Swarm.PrivateNetworkKey = new PreSharedKey().Generate(); + await _dfs1.StartAsync(); + } + } + + [Test] + public async Task LocalPeer() + { + Task[] tasks = + { + Task.Run(() => _dfs1.LocalPeer), + Task.Run(() => _dfs1.LocalPeer) + }; + var r = await Task.WhenAll(tasks); + Assert.AreEqual(r[0], r[1]); + } + + [Test] + public async Task KeyChain() + { + Task[] tasks = + { + Task.Run(() => _dfs1.KeyApi), + Task.Run(() => _dfs1.KeyApi) + }; + var r = await Task.WhenAll(tasks); + Assert.AreEqual(r[0], r[1]); + } + + //todo + //[Test] + //public async Task KeyChain_GetKey() + //{ + // var keyChain = await _dfs1.KeyChainAsync(); + // var key = await keyChain.GetPrivateKeyAsync(KeyRegistryTypes.DefaultKey); + // Assert.NotNull(key); + // Assert.True(key.IsPrivate); + //} + + [Test] + public async Task Swarm_Gets_Bootstrap_Peers() + { + var bootPeers = (await _dfs1.BootstrapApi.ListAsync()).ToArray(); + await _dfs1.StartAsync(); + try + { + var swarm = _dfs1.SwarmService; + var knownPeers = swarm.KnownPeerAddresses.ToArray(); + var endTime = DateTime.Now.AddSeconds(3); + while (true) + { + if (DateTime.Now > endTime) + { + throw new Exception("Bootstrap peers are not known."); + } + + if (bootPeers.All(a => knownPeers.Contains(a))) + { + break; + } + + await Task.Delay(50); + knownPeers = swarm.KnownPeerAddresses.ToArray(); + } + } + finally + { + await _dfs1.StopAsync(); + } + } + + [Test] + public async Task Start_NoListeners() + { + var swarm = await _dfs1.ConfigApi.GetAsync("Addresses.Swarm"); + try + { + await _dfs1.ConfigApi.SetAsync("Addresses.Swarm", "[]"); + await _dfs1.StartAsync(); + } + finally + { + await _dfs1.StopAsync(); + await _dfs1.ConfigApi.SetAsync("Addresses.Swarm", swarm); + } + } + + [Test] + public async Task Start_InvalidListener() + { + var swarm = await _dfs1.ConfigApi.GetAsync("Addresses.Swarm"); + try + { + // 1 - missing ip address + // 2 - invalid protocol name + // 3 - okay + var values = JToken.Parse("['/tcp/0', '/foo/bar', '/ip4/0.0.0.0/tcp/0']"); + await _dfs1.ConfigApi.SetAsync("Addresses.Swarm", values); + await _dfs1.StartAsync(); + } + finally + { + await _dfs1.StopAsync(); + await _dfs1.ConfigApi.SetAsync("Addresses.Swarm", swarm); + } + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _dfs1.Dispose(); + } + + base.Dispose(disposing); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DfsTests.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DfsTests.cs deleted file mode 100644 index d7e0123144..0000000000 --- a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DfsTests.cs +++ /dev/null @@ -1,140 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.Hashing; -using Catalyst.Abstractions.Types; -using Catalyst.Core.Modules.Hashing; -using Catalyst.TestUtils; -using FluentAssertions; -using NSubstitute; -using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using Xunit.Abstractions; - -namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests -{ - public sealed class DfsTests : FileSystemBasedTest - { - private readonly IpfsAdapter _ipfs; - private readonly ILogger _logger; - private readonly ITestOutputHelper _output; - private readonly IHashProvider _hashProvider; - - public DfsTests(ITestOutputHelper output) : base(output) - { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - - _output = output; - var passwordReader = Substitute.For(); - passwordReader.RetrieveOrPromptAndAddPasswordToRegistry(Arg.Any(), Arg.Any()) - .Returns(TestPasswordReader.BuildSecureStringPassword("abcd")); - - _logger = Substitute.For(); - _ipfs = new IpfsAdapter(passwordReader, FileSystem, _logger); - - // Starting IPFS takes a few seconds. Do it here, so that individual - // test times are not affected. - _ipfs.Generic.IdAsync() - .ConfigureAwait(false) - .GetAwaiter() - .GetResult(); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public async Task DFS_should_add_and_read_text() - { - var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); - - const string text = "good morning"; - var dfs = new Dfs(_ipfs, _hashProvider, _logger); - var id = await dfs.AddTextAsync(text, cts.Token); - var content = await dfs.ReadTextAsync(id, cts.Token); - - content.Should().Be(text); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public async Task DFS_should_add_and_read_binary() - { - var cts = new CancellationTokenSource(TimeSpan.FromSeconds(15)); - var binary = new byte[] - { - 1, 2, 3 - }; - var ms = new MemoryStream(binary); - var dfs = new Dfs(_ipfs, _hashProvider, _logger); - var id = await dfs.AddAsync(ms, "", cts.Token); - using (var stream = await dfs.ReadAsync(id, cts.Token)) - { - var content = new byte[binary.Length]; - await stream.ReadAsync(content, 0, content.Length, cts.Token); - content.Should().Equal(binary); - } - } - - [Fact(Skip = "waiting for Dns seed fix: https://github.com/catalyst-network/Catalyst.Framework/issues/1075")] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public async Task DFS_should_connect_to_a_seednode() - { - var seeds = (await _ipfs.Bootstrap.ListAsync().ConfigureAwait(false)) - .Select(a => a.PeerId) - .ToArray(); - Assert.True(seeds.Length > 0, "no seed nodes defined"); - - // Wait for a connection to a seed node. - var start = DateTime.Now; - var end = DateTime.Now.AddSeconds(15); - var found = false; - while (!found) - { - Assert.True(DateTime.Now <= end, "timeout"); - var peers = await _ipfs.Swarm.PeersAsync().ConfigureAwait(false); - found = peers.Any(p => seeds.Contains(p.Id)); - await Task.Delay(100).ConfigureAwait(false); - } - - _output.WriteLine( - $"Found in {(DateTime.Now - start).TotalSeconds.ToString(CultureInfo.InvariantCulture)} seconds."); - } - - protected override void Dispose(bool disposing) - { - if (disposing) - { - _ipfs?.Dispose(); - } - - base.Dispose(disposing); - } - } -} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DiscoveryOptionsTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DiscoveryOptionsTest.cs new file mode 100644 index 0000000000..2000c744a1 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/DiscoveryOptionsTest.cs @@ -0,0 +1,39 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Options; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +{ + public class DiscoveryOptionsTest + { + [Test] + public void Defaults() + { + var options = new DiscoveryOptions(); + Assert.Null(options.BootstrapPeers); + Assert.False(options.DisableMdns); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/Ed25519NodeTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/Ed25519NodeTest.cs new file mode 100644 index 0000000000..a98906d057 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/Ed25519NodeTest.cs @@ -0,0 +1,116 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +// using System.IO; +// using System.Linq; +// using System.Threading.Tasks; +// using Catalyst.Abstractions.Dfs; +// using Catalyst.Core.Lib.Cryptography; +// using Catalyst.Core.Modules.Dfs.Tests.Utils; +// using Catalyst.Core.Modules.Hashing; +// using Catalyst.TestUtils; +// using MultiFormats.Registry; +// using Newtonsoft.Json.Linq; +// using NUnit.Framework; +// +// +// namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +// { +// public class Ed25519NodeTest +// { +// private IDfsService ipfs; +// +// public Ed25519NodeTest(TestContext output) +// { +// ipfs = new TestFixture(output).Ipfs; +// } +// +// [Test] +// public async Task Can_Create() +// { +// var ed = await CreateNode(); +// try +// { +// Assert.NotNull(ed); +// var node = await ed.LocalPeer; +// Assert.NotNull(node); +// } +// finally +// { +// DeleteNode(ed); +// } +// } +// +// [Test] +// public async Task CanConnect() +// { +// var ed = await CreateNode(); +// try +// { +// await ed.StartAsync(); +// var node = await ed.LocalPeer; +// Assert.AreNotEqual(0, node.Addresses.Count()); +// var addr = node.Addresses.First(); +// await ipfs.StartAsync(); +// try +// { +// await ipfs.Swarm.ConnectAsync(addr); +// var peers = await ipfs.Swarm.PeersAsync(); +// Assert.True(peers.Any(p => p.Id == addr.Address)); +// await ipfs.Swarm.DisconnectAsync(addr); +// } +// finally +// { +// await ipfs.StopAsync(); +// } +// } +// finally +// { +// await ed.StopAsync(); +// DeleteNode(ed); +// } +// } +// +// async Task CreateNode() +// { +// var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); +// var testPasswordManager = new PasswordManager(new TestPasswordReader(), new PasswordRegistry()); +// var ipfs = new DfsService(hashProvider, testPasswordManager); +// ipfs.Options.Repository.Folder = Path.Combine(Path.GetTempPath(), "ipfs-ed255129-test"); +// ipfs.Options.KeyChain.DefaultKeyType = "ed25519"; +// await ipfs.Config.SetAsync( +// "Addresses.Swarm", +// JToken.FromObject(new string[] {"/ip4/0.0.0.0/tcp/0"}) +// ); +// return ipfs; +// } +// +// void DeleteNode(DfsService ipfs) +// { +// if (Directory.Exists(ipfs.Options.Repository.Folder)) +// { +// Directory.Delete(ipfs.Options.Repository.Folder, true); +// } +// } +// } +// } diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/ExceptionAssert.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/ExceptionAssert.cs new file mode 100644 index 0000000000..727c9f722b --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/ExceptionAssert.cs @@ -0,0 +1,74 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +{ + /// + /// Asserting an . + /// + internal static class ExceptionAssert + { + internal static T Throws(Action action, string expectedMessage = null) where T : Exception + { + try + { + action(); + } + catch (AggregateException e) + { + var match = e.InnerExceptions.OfType().FirstOrDefault(); + if (match == null) + { + throw; + } + + if (expectedMessage != null) + { + Assert.AreEqual(expectedMessage, match.Message); + } + + return match; + } + catch (T e) + { + if (expectedMessage != null) + { + Assert.AreEqual(expectedMessage, e.Message); + } + + return e; + } + + throw new Exception($"Exception of type {typeof(T)}should be thrown."); + } + + public static Exception Throws(Action action, string expectedMessage = null) + { + return Throws(action, expectedMessage); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/IpfsAdapterTests.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/IpfsAdapterTests.cs deleted file mode 100644 index d0b722c562..0000000000 --- a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/IpfsAdapterTests.cs +++ /dev/null @@ -1,94 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System.IO; -using System.Linq; -using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.Types; -using Catalyst.Core.Lib.Config; -using Catalyst.TestUtils; -using FluentAssertions; -using NSubstitute; -using Serilog; -using Xunit; -using Xunit.Abstractions; - -namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests -{ - public sealed class IpfsAdapterTests : FileSystemBasedTest - { - private readonly IPasswordManager _passwordManager; - private readonly ILogger _logger; - - public IpfsAdapterTests(ITestOutputHelper output) : base(output) - { - _passwordManager = Substitute.For(); - _passwordManager.RetrieveOrPromptAndAddPasswordToRegistry(PasswordRegistryTypes.IpfsPassword, - Arg.Any()).ReturnsForAnyArgs( - TestPasswordReader.BuildSecureStringPassword("abcd")); - - _logger = Substitute.For(); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public void Constructor_should_read_seed_servers_addresses_from_peerSettings() - { - using (var ipfs = new IpfsAdapter(_passwordManager, FileSystem, _logger)) - { - ipfs.Options.Discovery.BootstrapPeers.Count().Should().NotBe(0); - } - } - - [Fact] - public void Constructor_should_read_a_password() - { - using (new IpfsAdapter(_passwordManager, FileSystem, _logger)) - { - _passwordManager.ReceivedWithAnyArgs(1) - .RetrieveOrPromptAndAddPasswordToRegistry(PasswordRegistryTypes.IpfsPassword); - } - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public void Constructor_should_use_ipfs_subfolder() - { - using (var ipfs = new IpfsAdapter(_passwordManager, FileSystem, _logger)) - { - ipfs.Options.Repository.Folder.Should() - .Be(Path.Combine(FileSystem.GetCatalystDataDir().FullName, Constants.DfsDataSubDir)); - } - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public void Constructor_should_use_ipfs_private_network() - { - using (var ipfs = new IpfsAdapter(_passwordManager, FileSystem, _logger)) - { - ipfs.Options.Swarm.PrivateNetworkKey.Should().NotBeNull(); - } - } - } -} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/LinkedData/ProtobufFormatTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/LinkedData/ProtobufFormatTest.cs new file mode 100644 index 0000000000..93603dee27 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/LinkedData/ProtobufFormatTest.cs @@ -0,0 +1,93 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Text; +using Catalyst.Core.Lib.Dag; +using Catalyst.Core.Modules.Dfs.LinkedData; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.LinkedData +{ + public class ProtobufFormatTest + { + private ILinkedDataFormat formatter = new ProtobufFormat(); + + [Test] + public void Empty() + { + var data = new byte[0]; + var node = new DagNode(data); + + var cbor = formatter.Deserialise(node.ToArray()); + Assert.AreEqual(data, cbor["data"].GetByteString()); + Assert.AreEqual(0, cbor["links"].Values.Count); + + var node1 = formatter.Serialize(cbor); + Assert.AreEqual(node.ToArray(), node1); + } + + [Test] + public void DataOnly() + { + var data = Encoding.UTF8.GetBytes("abc"); + var node = new DagNode(data); + + var cbor = formatter.Deserialise(node.ToArray()); + Assert.AreEqual(data, cbor["data"].GetByteString()); + Assert.AreEqual(0, cbor["links"].Values.Count); + + var node1 = formatter.Serialize(cbor); + Assert.AreEqual(node.ToArray(), node1); + } + + [Test] + public void LinksOnly() + { + var a = Encoding.UTF8.GetBytes("a"); + var anode = new DagNode(a); + var alink = anode.ToLink("a"); + + var b = Encoding.UTF8.GetBytes("b"); + var bnode = new DagNode(b); + var blink = bnode.ToLink(); + + var node = new DagNode(null, new[] {alink, blink}); + var cbor = formatter.Deserialise(node.ToArray()); + + Assert.AreEqual(2, cbor["links"].Values.Count); + + var link = cbor["links"][0]; + Assert.AreEqual("QmYpoNmG5SWACYfXsDztDNHs29WiJdmP7yfcMd7oVa75Qv", link["Cid"]["/"].AsString()); + Assert.AreEqual("", link["Name"].AsString()); + Assert.AreEqual(3, link["Size"].AsInt32()); + + link = cbor["links"][1]; + Assert.AreEqual("QmQke7LGtfu3GjFP3AnrP8vpEepQ6C5aJSALKAq653bkRi", link["Cid"]["/"].AsString()); + Assert.AreEqual("a", link["Name"].AsString()); + Assert.AreEqual(3, link["Size"].AsInt32()); + + var node1 = formatter.Serialize(cbor); + Assert.AreEqual(node.ToArray(), node1); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/LinkedData/RawFormatTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/LinkedData/RawFormatTest.cs new file mode 100644 index 0000000000..86d6bfb1b9 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/LinkedData/RawFormatTest.cs @@ -0,0 +1,58 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Text; +using Catalyst.Core.Modules.Dfs.LinkedData; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.LinkedData +{ + public class RawFormatTest + { + private ILinkedDataFormat formatter = new RawFormat(); + + [Test] + public void Empty() + { + var data = new byte[0]; + + var cbor = formatter.Deserialise(data); + Assert.AreEqual(data, cbor["data"].GetByteString()); + + var data1 = formatter.Serialize(cbor); + Assert.AreEqual(data, data1); + } + + [Test] + public void Data() + { + var data = Encoding.UTF8.GetBytes("abc"); + + var cbor = formatter.Deserialise(data); + Assert.AreEqual(data, cbor["data"].GetByteString()); + + var data1 = formatter.Serialize(cbor); + Assert.AreEqual(data, data1); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/Migration/MigrationManagerTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/Migration/MigrationManagerTest.cs new file mode 100644 index 0000000000..2475b54865 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/Migration/MigrationManagerTest.cs @@ -0,0 +1,73 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Modules.Dfs.Migration; +using Catalyst.TestUtils; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.Migration +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class MigrationManagerTest + { + private readonly IDfsService _dfs; + + public MigrationManagerTest() + { + _dfs = TestDfs.GetTestDfs(); + } + + [Test] + public void HasMigrations() + { + var migrator = new MigrationManager(_dfs.Options.Repository); + var migrations = migrator.Migrations; + Assert.AreNotEqual(0, migrations.Count); + } + + [Test] + public void MirgrateToUnknownVersion() + { + var migrator = new MigrationManager(_dfs.Options.Repository); + ExceptionAssert.Throws(() => + { + migrator.MirgrateToVersionAsync(int.MaxValue).Wait(); + }); + } + + [Test] + public async Task MigrateToLowestThenHighest() + { + var migrator = new MigrationManager(_dfs.Options.Repository); + await migrator.MirgrateToVersionAsync(0); + Assert.AreEqual(0, migrator.CurrentVersion); + + await migrator.MirgrateToVersionAsync(migrator.LatestVersion); + Assert.AreEqual(migrator.LatestVersion, migrator.CurrentVersion); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/NamedContentTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/NamedContentTest.cs new file mode 100644 index 0000000000..6f56b0dc6c --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/NamedContentTest.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Dfs; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +{ + public class NamedContentTest + { + [Test] + public void Properties() + { + var nc = new NamedContent + { + ContentPath = "/ipfs/...", + NamePath = "/ipns/..." + }; + Assert.AreEqual("/ipfs/...", nc.ContentPath); + Assert.AreEqual("/ipns/...", nc.NamePath); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/NodeFileTransferTests.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/NodeFileTransferTests.cs index af4c497b45..a076efcb33 100644 --- a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/NodeFileTransferTests.cs +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/NodeFileTransferTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,153 +21,174 @@ #endregion -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.Dfs; -using Catalyst.Abstractions.FileTransfer; -using Catalyst.Abstractions.Rpc; -using Catalyst.Abstractions.Types; -using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.FileTransfer; -using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Modules.Hashing; -using Catalyst.Core.Modules.Rpc.Server.IO.Observers; -using Catalyst.Protocol.Rpc.Node; -using Catalyst.TestUtils; -using DotNetty.Transport.Channels; -using NSubstitute; -using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using Xunit.Abstractions; - -namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests -{ - public sealed class NodeFileTransferTests : FileSystemBasedTest - { - private readonly ILogger _logger; - private readonly IChannelHandlerContext _fakeContext; - private readonly IDownloadFileTransferFactory _nodeFileTransferFactory; - private readonly IDfs _dfs; - - public NodeFileTransferTests(ITestOutputHelper testOutput) : base(testOutput) - { - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - - _logger = Substitute.For(); - _fakeContext = Substitute.For(); - _nodeFileTransferFactory = new DownloadFileTransferFactory(_logger); - - var passwordManager = Substitute.For(); - passwordManager - .RetrieveOrPromptAndAddPasswordToRegistry(PasswordRegistryTypes.IpfsPassword, Arg.Any()) - .Returns(TestPasswordReader.BuildSecureStringPassword("abcd")); - - var ipfsEngine = new IpfsAdapter(passwordManager, FileSystem, _logger); - - _logger = Substitute.For(); - _dfs = new Dfs(ipfsEngine, hashProvider, _logger); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public async Task Cancel_File_Transfer() - { - var sender = PeerIdHelper.GetPeerId("sender"); - - IDownloadFileInformation fileTransferInformation = new DownloadFileTransferInformation( - sender, - sender, - _fakeContext.Channel, - CorrelationId.GenerateCorrelationId(), - string.Empty, - 555); - - var cancellationTokenSource = new CancellationTokenSource(); - _nodeFileTransferFactory.RegisterTransfer(fileTransferInformation); - _nodeFileTransferFactory - .FileTransferAsync(fileTransferInformation.CorrelationId, cancellationTokenSource.Token) - .ConfigureAwait(false).GetAwaiter(); - Assert.Single(_nodeFileTransferFactory.Keys); - cancellationTokenSource.Cancel(); - - await TaskHelper.WaitForAsync(() => fileTransferInformation.IsCompleted, TimeSpan.FromSeconds(10)); - - var fileCleanedUp = !File.Exists(fileTransferInformation.TempPath); - - Assert.True(fileTransferInformation.IsExpired()); - Assert.True(fileCleanedUp); - Assert.Empty(_nodeFileTransferFactory.Keys); - } - - [Theory] - [Trait(Traits.TestType, Traits.IntegrationTest)] - [InlineData(1000L)] - [InlineData(82000L)] - [InlineData(100000L)] - public async Task Verify_File_Integrity_On_Transfer(long byteSize) - { - await AddFileToDfs(byteSize).ConfigureAwait(false); - } - - private async Task AddFileToDfs(long byteSize) - { - var fakeNode = Substitute.For(); - var sender = PeerIdHelper.GetPeerId("sender"); - var recipient = PeerIdHelper.GetPeerId("recipient"); - var senderPeerId = sender; - var peerSettings = senderPeerId.ToSubstitutedPeerSettings(); - var recipientPeerId = recipient; - var fileToTransfer = FileHelper.CreateRandomTempFile(byteSize); - var addFileToDfsRequestHandler = - new AddFileToDfsRequestObserver(_dfs, peerSettings, _nodeFileTransferFactory, _logger); - var transferBytesRequestHandler = - new TransferFileBytesRequestObserver(_nodeFileTransferFactory, peerSettings, _logger); - - var uniqueFileKey = CorrelationId.GenerateCorrelationId(); - var crcValue = FileHelper.GetCrcValue(fileToTransfer); - - //Create a response object and set its return value - var request = new AddFileToDfsRequest - { - Node = "node1", - FileName = fileToTransfer, - FileSize = (ulong) byteSize - }.ToProtocolMessage(sender, uniqueFileKey); - request.SendToHandler(_fakeContext, addFileToDfsRequestHandler); - - Assert.Single(_nodeFileTransferFactory.Keys); - - var fileTransferInformation = - _nodeFileTransferFactory.GetFileTransferInformation(uniqueFileKey); - Assert.True(fileTransferInformation.Initialised, "File transfer not initialised"); - - using (var fs = File.Open(fileToTransfer, FileMode.Open)) - { - var fileUploadInformation = new UploadFileTransferInformation(fs, senderPeerId, recipientPeerId, - fakeNode.Channel, uniqueFileKey); - for (uint i = 0; i < fileTransferInformation.MaxChunk; i++) - { - fileUploadInformation.GetUploadMessageDto(i).Content - .SendToHandler(_fakeContext, transferBytesRequestHandler); - } - } - - Assert.True(fileTransferInformation.ChunkIndicatorsTrue()); - - await TaskHelper.WaitForAsync(() => fileTransferInformation.DfsHash != null, TimeSpan.FromSeconds(15)).ConfigureAwait(false); - Assert.NotNull(fileTransferInformation.DfsHash); - - long ipfsCrcValue; - using (var ipfsStream = await _dfs.ReadAsync(fileTransferInformation.DfsHash).ConfigureAwait(false)) - { - ipfsCrcValue = FileHelper.GetCrcValue(ipfsStream); - } - - Assert.Equal(crcValue, ipfsCrcValue); - } - } -} +// #region LICENSE +// +// /** +// * Copyright (c) 2022 Catalyst Network +// * +// * This file is part of Catalyst.Node +// * +// * Catalyst.Node is free software: you can redistribute it and/or modify +// * it under the terms of the GNU General Public License as published by +// * the Free Software Foundation, either version 2 of the License, or +// * (at your option) any later version. +// * +// * Catalyst.Node is distributed in the hope that it will be useful, +// * but WITHOUT ANY WARRANTY; without even the implied warranty of +// * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// * GNU General Public License for more details. +// * +// * You should have received a copy of the GNU General Public License +// * along with Catalyst.Node. If not, see . +// */ +// +// #endregion +// +// using System; +// using System.IO; +// using System.Threading; +// using System.Threading.Tasks; +// using Catalyst.Abstractions.Cryptography; +// using Catalyst.Abstractions.Dfs; +// using Catalyst.Abstractions.FileTransfer; +// using Catalyst.Abstractions.Rpc; +// using Catalyst.Abstractions.Types; +// using Catalyst.Core.Lib.Cryptography; +// using Catalyst.Core.Lib.Extensions; +// using Catalyst.Core.Lib.FileTransfer; +// using Catalyst.Core.Lib.IO.Messaging.Correlation; +// using Catalyst.Core.Modules.Hashing; +// using Catalyst.Core.Modules.Rpc.Server.IO.Observers; +// using Catalyst.Protocol.Rpc.Node; +// using Catalyst.TestUtils; +// using DotNetty.Transport.Channels; +// using MultiFormats.Registry; +// using NSubstitute; +// using Serilog; +// using NUnit.Framework; +// +// +// namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +// { +// public sealed class NodeFileTransferTests : FileSystemBasedTest +// { +// private readonly ILogger _logger; +// private readonly IChannelHandlerContext _fakeContext; +// private readonly IDownloadFileTransferFactory _nodeFileTransferFactory; +// private readonly IDfsService _dfsService; +// +// public NodeFileTransferTests(TestContext testOutput) : base(testOutput) +// { +// _logger = Substitute.For(); +// _fakeContext = Substitute.For(); +// _nodeFileTransferFactory = new DownloadFileTransferFactory(_logger); +// +// var passwordManager = Substitute.For(); +// passwordManager +// .RetrieveOrPromptAndAddPasswordToRegistry(PasswordRegistryTypes.DefaultNodePassword, Arg.Any()) +// .Returns(TestPasswordReader.BuildSecureStringPassword("abcd")); +// +// var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); +// var testPasswordManager = new PasswordManager(new TestPasswordReader(), new PasswordRegistry()); +// _dfsService = new DfsService(hashProvider, testPasswordManager); +// } +// +// [Test] +// +// public async Task Cancel_File_Transfer() +// { +// var sender = MultiAddressHelper.GetAddress("sender"); +// +// IDownloadFileInformation fileTransferInformation = new DownloadFileTransferInformation( +// sender, +// sender, +// _fakeContext.Channel, +// CorrelationId.GenerateCorrelationId(), +// string.Empty, +// 555); +// +// var cancellationTokenSource = new CancellationTokenSource(); +// _nodeFileTransferFactory.RegisterTransfer(fileTransferInformation); +// _nodeFileTransferFactory +// .FileTransferAsync(fileTransferInformation.CorrelationId, cancellationTokenSource.Token) +// .ConfigureAwait(false).GetAwaiter(); +// Assert.Single(_nodeFileTransferFactory.Keys); +// cancellationTokenSource.Cancel(); +// +// await TaskHelper.WaitForAsync(() => fileTransferInformation.IsCompleted, TimeSpan.FromSeconds(10)); +// +// var fileCleanedUp = !File.Exists(fileTransferInformation.TempPath); +// +// Assert.True(fileTransferInformation.IsExpired()); +// Assert.True(fileCleanedUp); +// Assert.Empty(_nodeFileTransferFactory.Keys); +// } +// +// [Theory] +// +// [TestCase(1000L)] +// [TestCase(82000L)] +// [TestCase(100000L)] +// public async Task Verify_File_Integrity_On_Transfer(long byteSize) +// { +// await AddFileToDfs(byteSize).ConfigureAwait(false); +// } +// +// private async Task AddFileToDfs(long byteSize) +// { +// var fakeNode = Substitute.For(); +// var sender = MultiAddressHelper.GetAddress("sender"); +// var recipient = MultiAddressHelper.GetAddress("recipient"); +// var sender = sender; +// var peerSettings = sender.ToSubstitutedPeerSettings(); +// var recipientPeerId = recipient; +// var fileToTransfer = FileHelper.CreateRandomTempFile(byteSize); +// var addFileToDfsRequestHandler = +// new AddFileToDfsRequestObserver(_dfsService, peerSettings, _nodeFileTransferFactory, _logger); +// var transferBytesRequestHandler = +// new TransferFileBytesRequestObserver(_nodeFileTransferFactory, peerSettings, _logger); +// +// var uniqueFileKey = CorrelationId.GenerateCorrelationId(); +// var crcValue = FileHelper.GetCrcValue(fileToTransfer); +// +// //Create a response object and set its return value +// var request = new AddFileToDfsRequest +// { +// Node = "node1", +// FileName = fileToTransfer, +// FileSize = (ulong) byteSize +// }.ToProtocolMessage(sender, uniqueFileKey); +// request.SendToHandler(_fakeContext, addFileToDfsRequestHandler); +// +// Assert.Single(_nodeFileTransferFactory.Keys); +// +// var fileTransferInformation = +// _nodeFileTransferFactory.GetFileTransferInformation(uniqueFileKey); +// Assert.True(fileTransferInformation.Initialised, "File transfer not initialised"); +// +// using (var fs = File.Open(fileToTransfer, FileMode.Open)) +// { +// var fileUploadInformation = new UploadFileTransferInformation(fs, sender, recipientPeerId, +// fakeNode.Channel, uniqueFileKey); +// for (uint i = 0; i < fileTransferInformation.MaxChunk; i++) +// { +// fileUploadInformation.GetUploadMessageDto(i).Content +// .SendToHandler(_fakeContext, transferBytesRequestHandler); +// } +// } +// +// Assert.True(fileTransferInformation.ChunkIndicatorsTrue()); +// +// await TaskHelper.WaitForAsync(() => fileTransferInformation.DfsHash != null, TimeSpan.FromSeconds(15)).ConfigureAwait(false); +// Assert.NotNull(fileTransferInformation.DfsHash); +// +// long ipfsCrcValue; +// using (var ipfsStream = await _dfsService.FileSystem.ReadFileAsync(fileTransferInformation.DfsHash).ConfigureAwait(false)) +// { +// ipfsCrcValue = FileHelper.GetCrcValue(ipfsStream); +// } +// +// Assert.AreEqual(crcValue, ipfsCrcValue); +// } +// } +// } diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/ProtobufTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/ProtobufTest.cs new file mode 100644 index 0000000000..75fd26870d --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/ProtobufTest.cs @@ -0,0 +1,80 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using ProtoBuf; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +{ + [ProtoContract] + internal class M1 + { + [ProtoMember(1, IsRequired = false)] + public byte[] Data; + } + + [ProtoContract] + internal class M2 + { + [ProtoMember(1, IsRequired = false)] + public ArraySegment? Data; + } + + // [TestClass] + // [Ignore("https://github.com/mgravell/protobuf-net/issues/368")] + // public class ProtobufTest + // { + // [Test] + // public void NullData() + // { + // var m1 = new M1(); + // var ms1 = new MemoryStream(); + // Serializer.Serialize(ms1, m1); + // var bytes1 = ms1.ToArray(); + // + // var m2 = new M2(); + // var ms2 = new MemoryStream(); + // Serializer.Serialize(ms2, m2); + // var bytes2 = ms2.ToArray(); + // + // Assert.AreEqual(bytes1, bytes2); + // } + // + // [Test] + // public void SomeData() + // { + // var data = new byte[] {10, 11, 12}; + // var m1 = new M1 {Data = data}; + // var ms1 = new MemoryStream(); + // Serializer.Serialize(ms1, m1); + // var bytes1 = ms1.ToArray(); + // + // var m2 = new M2 {Data = new ArraySegment(data)}; + // var ms2 = new MemoryStream(); + // Serializer.Serialize(ms2, m2); + // var bytes2 = ms2.ToArray(); + // + // Assert.AreEqual(bytes1, bytes2); + // } + // } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/RandomWalkTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/RandomWalkTest.cs new file mode 100644 index 0000000000..a02d72f3d2 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/RandomWalkTest.cs @@ -0,0 +1,62 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading.Tasks; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +{ + public class RandomWalkTest + { + [Test] + public async Task CanStartAndStop() + { + var walk = new RandomWalk(); + await walk.StartAsync(); + await walk.StopAsync(); + + await walk.StartAsync(); + await walk.StopAsync(); + } + + [Test] + public void CannotStartTwice() + { + var walk = new RandomWalk(); + walk.StartAsync().Wait(); + ExceptionAssert.Throws(() => { walk.StartAsync().Wait(); }); + } + + [Test] + public async Task CanStopMultipletimes() + { + var walk = new RandomWalk(); + await walk.StartAsync(); + await walk.StopAsync(); + await walk.StopAsync(); + await walk.StartAsync(); + await walk.StopAsync(); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/RepositoryOptionsTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/RepositoryOptionsTest.cs new file mode 100644 index 0000000000..251ea0605a --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/RepositoryOptionsTest.cs @@ -0,0 +1,134 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using Catalyst.Abstractions.Options; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +{ + public class RepositoryOptionsTest + { + [Test] + public void Defaults() + { + var options = new RepositoryOptions(); + Assert.NotNull(options.Folder); + } + + [Test] + public void Environment_Home() + { + var names = new[] {"IPFS_PATH", "HOME", "HOMEPATH"}; + var values = names.Select(Environment.GetEnvironmentVariable); + var sep = Path.DirectorySeparatorChar; + try + { + foreach (var name in names) + { + Environment.SetEnvironmentVariable(name, null); + } + + Environment.SetEnvironmentVariable("HOME", $"{sep}home1"); + var options = new RepositoryOptions(); + Assert.AreEqual($"{sep}home1{sep}.catalyst", options.Folder); + + Environment.SetEnvironmentVariable("HOME", $"{sep}home2{sep}"); + options = new RepositoryOptions(); + Assert.AreEqual($"{sep}home2{sep}.catalyst", options.Folder); + } + finally + { + var pairs = names.Zip(values, (name, value) => new {name, value}); + foreach (var pair in pairs) + { + Environment.SetEnvironmentVariable(pair.name, pair.value); + } + } + } + + [Test] + public void Environment_HomePath() + { + var names = new[] {"IPFS_PATH", "HOME", "HOMEPATH"}; + var values = names.Select(Environment.GetEnvironmentVariable); + var sep = Path.DirectorySeparatorChar; + try + { + foreach (var name in names) + { + Environment.SetEnvironmentVariable(name, null); + } + + Environment.SetEnvironmentVariable("HOMEPATH", $"{sep}home1"); + var options = new RepositoryOptions(); + Assert.AreEqual($"{sep}home1{sep}.catalyst", options.Folder); + + Environment.SetEnvironmentVariable("HOMEPATH", $"{sep}home2{sep}"); + options = new RepositoryOptions(); + Assert.AreEqual($"{sep}home2{sep}.catalyst", options.Folder); + } + finally + { + var pairs = names.Zip(values, (name, value) => new {name, value}); + foreach (var pair in pairs) + { + Environment.SetEnvironmentVariable(pair.name, pair.value); + } + } + } + + [Test] + public void Environment_IpfsPath() + { + var names = new[] {"IPFS_PATH", "HOME", "HOMEPATH"}; + var values = names.Select(Environment.GetEnvironmentVariable); + var sep = Path.DirectorySeparatorChar; + try + { + foreach (var name in names) + { + Environment.SetEnvironmentVariable(name, null); + } + + Environment.SetEnvironmentVariable("IPFS_PATH", $"{sep}x1"); + var options = new RepositoryOptions(); + Assert.AreEqual($"{sep}x1", options.Folder); + + Environment.SetEnvironmentVariable("IPFS_PATH", $"{sep}x2{sep}"); + options = new RepositoryOptions(); + Assert.AreEqual($"{sep}x2{sep}", options.Folder); + } + finally + { + var pairs = names.Zip(values, (name, value) => new {name, value}); + foreach (var pair in pairs) + { + Environment.SetEnvironmentVariable(pair.name, pair.value); + } + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/SwarmOptionsTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/SwarmOptionsTest.cs new file mode 100644 index 0000000000..412732a9ff --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/SwarmOptionsTest.cs @@ -0,0 +1,39 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Options; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests +{ + public class SwarmOptionsTest + { + [Test] + public void Defaults() + { + var options = new SwarmOptions(); + Assert.Null(options.PrivateNetworkKey); + Assert.AreEqual(8, options.MinConnections); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/UnixFileSystem/UnixFsNodeTest.cs b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/UnixFileSystem/UnixFsNodeTest.cs new file mode 100644 index 0000000000..acd74a0c78 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/UnixFileSystem/UnixFsNodeTest.cs @@ -0,0 +1,53 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Core.Modules.Dfs.UnixFs; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Dfs.Tests.IntegrationTests.UnixFileSystem +{ + public class UnixFsNodeTest + { + [Test] + public void ToLink() + { + var node = new UnixFsNode + { + Name = "bar", + Id = "Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD", + IsDirectory = true, + Size = 10, + DagSize = 16 + }; + var link = node.ToLink("foo"); + Assert.AreEqual(node.Id, link.Id); + Assert.AreEqual(node.DagSize, link.Size); + Assert.AreEqual("foo", link.Name); + + link = node.ToLink(); + Assert.AreEqual(node.Id, link.Id); + Assert.AreEqual(node.DagSize, link.Size); + Assert.AreEqual("bar", link.Name); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/star_trails.mp4 b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/star_trails.mp4 new file mode 100644 index 0000000000..a12b7a1a54 Binary files /dev/null and b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/star_trails.mp4 differ diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/starx2.mp4 b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/starx2.mp4 new file mode 100644 index 0000000000..101c3506eb Binary files /dev/null and b/src/Catalyst.Core.Modules.Dfs.Tests/IntegrationTests/starx2.mp4 differ diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/UnitTests/DfsTests.cs b/src/Catalyst.Core.Modules.Dfs.Tests/UnitTests/DfsTests.cs deleted file mode 100644 index c38cb6373f..0000000000 --- a/src/Catalyst.Core.Modules.Dfs.Tests/UnitTests/DfsTests.cs +++ /dev/null @@ -1,130 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Util; -using Catalyst.Core.Modules.Hashing; -using FluentAssertions; -using LibP2P; -using NSubstitute; -using Serilog; -using TheDotNetLeague.Ipfs.Abstractions; -using TheDotNetLeague.Ipfs.Core.Lib; -using TheDotNetLeague.Ipfs.Core.Lib.CoreApi; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; - -namespace Catalyst.Core.Modules.Dfs.Tests.UnitTests -{ - public sealed class DfsTests : IDisposable - { - private const int DelayInMs = 300; - private readonly ICoreApi _ipfsEngine; - private readonly Cid _expectedCid; - private readonly IFileSystemNode _addedRecord; - private readonly CancellationTokenSource _cancellationTokenSource; - private readonly Dfs _dfs; - private readonly HashProvider _hashProvider; - - public DfsTests() - { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - - _ipfsEngine = Substitute.For(); - var fileSystem = Substitute.For(); - _ipfsEngine.FileSystem.Returns(fileSystem); - - var logger = Substitute.For(); - _expectedCid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("data")); - - _addedRecord = Substitute.For(); - _addedRecord.Id.ReturnsForAnyArgs(_expectedCid); - _cancellationTokenSource = new CancellationTokenSource(TimeSpan.FromMilliseconds(DelayInMs)); - - _dfs = new Dfs(_ipfsEngine, _hashProvider, logger); - } - - [Fact] - public async Task AddTextAsync_Should_Rely_On_IpfsEngine_And_Return_Record_Id() - { - _ipfsEngine.FileSystem.AddTextAsync("good morning", Arg.Any(), Arg.Any()) - .Returns(c => Task.FromResult(_addedRecord)); - - var record = await _dfs.AddTextAsync("good morning"); - record.Should().Be(_expectedCid); - } - - [Fact] - public async Task AddAsync_Should_Rely_On_IpfsEngine_And_Return_Record_Id() - { - _ipfsEngine.FileSystem.AddAsync(Stream.Null, Arg.Any(), Arg.Any(), - Arg.Any()) - .Returns(c => Task.FromResult(_addedRecord)); - - var record = await _dfs.AddAsync(Stream.Null); - record.Should().Be(_expectedCid); - } - - [Fact] - public async Task ReadAsync_Should_Rely_On_IpfsEngine_And_Return_Streamed_Content() - { - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("file")); - _ipfsEngine.FileSystem - .ReadFileAsync(cid.Encode(), Arg.Any()) - .Returns(c => "the content".ToMemoryStream()); - - using (var stream = await _dfs.ReadAsync(cid.Encode())) - { - stream.ReadAllAsUtf8String(false).Should().Be("the content"); - } - } - - [Fact] - public async Task ReadTextAsync_Should_Rely_On_IpfsEngine_And_Return_Text_Content() - { - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("file")); - _ipfsEngine.FileSystem - .ReadAllTextAsync(cid.Encode(), Arg.Any()) - .Returns(c => "the other content"); - - var text = await _dfs.ReadTextAsync(cid.Encode()); - text.Should().Be("the other content"); - } - - private void Dispose(bool disposing) - { - if (!disposing) - { - return; - } - - _cancellationTokenSource?.Dispose(); - } - - public void Dispose() { Dispose(true); } - } -} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/UnitTests/NtDfsTests.cs b/src/Catalyst.Core.Modules.Dfs.Tests/UnitTests/NtDfsTests.cs deleted file mode 100644 index e9646f733a..0000000000 --- a/src/Catalyst.Core.Modules.Dfs.Tests/UnitTests/NtDfsTests.cs +++ /dev/null @@ -1,225 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.IO; -using System.IO.Abstractions; -using System.Linq; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Catalyst.Abstractions.Hashing; -using Catalyst.Core.Lib.Config; -using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Util; -using Catalyst.Core.Modules.Hashing; -using FluentAssertions; -using NSubstitute; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using IFileSystem = Catalyst.Abstractions.FileSystem.IFileSystem; - -namespace Catalyst.Core.Modules.Dfs.Tests.UnitTests -{ - public class DevDfsTests - { - private readonly IHashProvider _hashProvider; - private readonly IFileSystem _fileSystem; - private readonly DevDfs _dfs; - private readonly string _baseFolder; - - public DevDfsTests() - { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - _fileSystem = Substitute.For(); - var file = Substitute.For(); - _fileSystem.File.Returns(file); - _fileSystem.GetCatalystDataDir() - .Returns(new DirectoryInfo("correct-information")); - _dfs = new DevDfs(_fileSystem, _hashProvider); - - _baseFolder = Path.Combine(_fileSystem.GetCatalystDataDir().FullName, - Constants.DfsDataSubDir); - } - - [Fact] - public async Task AddAsync_Should_Be_Cancellable() - { - _fileSystem.File.Create(Arg.Any()).Returns(new MemoryStream()); - var contentStream = Substitute.For(); - var cancellationToken = new CancellationToken(); - await _dfs.AddAsync(contentStream, - cancellationToken: cancellationToken); - - await contentStream.Received(1).CopyToAsync(Arg.Any(), Arg.Is(cancellationToken)); - } - - [Fact] - public async Task AddAsync_Should_Save_File_In_Subfolder_With_Hash_As_Name() - { - _fileSystem.File.Create(Arg.Any()).Returns(new MemoryStream()); - - var contentBytes = BitConverter.GetBytes(123456); - var contentStream = contentBytes.ToMemoryStream(); - - var expectedCid = CidHelper.CreateCid(_hashProvider.ComputeMultiHash(contentBytes)); - - var cid = await _dfs.AddAsync(contentStream); - - cid.Should().Be(expectedCid); - } - - [Fact] - public async Task AddAsync_Should_Write_The_Correct_Content() - { - var content = "<:3)~~~~"; - var contentStream = content.ToMemoryStream(); - - var resultStream = new MemoryStream(); - - var mockFileStream = Substitute.For(); - mockFileStream.CanWrite.Returns(true); - mockFileStream.When(m => m.WriteAsync(Arg.Any(), Arg.Any(), Arg.Any())).Do(x => - resultStream.Write(x.ArgAt(0), x.ArgAt(1), x.ArgAt(2))); - - _fileSystem.File.Create(Arg.Any()).Returns(mockFileStream); - - await _dfs.AddAsync(contentStream); - - var resultBytes = await resultStream.ReadAllBytesAsync(CancellationToken.None); - var resultContent = Encoding.UTF8.GetString(resultBytes); - resultContent.Should().Be(content); - } - - [Fact] - public async Task AddTextAsync_Should_Be_Cancellable() - { - var cancellationToken = new CancellationToken(); - await _dfs.AddTextAsync("good morning", cancellationToken); - await _fileSystem.File.Received(1).WriteAllTextAsync( - Arg.Any(), - Arg.Any(), - Arg.Any(), - Arg.Is(cancellationToken)); - } - - [Fact] - public async Task AddTextAsync_Should_Save_File_In_Subfolder_With_Hash_As_Name() - { - var someGoodUtf8Content = "some good utf8 content!"; - - var cid = await _dfs.AddTextAsync(someGoodUtf8Content); - var expectedCid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash(someGoodUtf8Content)); - - await _fileSystem.File.Received(1).WriteAllTextAsync( - Arg.Is(Path.Combine(_baseFolder, expectedCid.Encode())), - Arg.Any(), - Arg.Is(Encoding.UTF8), - Arg.Any()); - - cid.Should().Be(expectedCid); - } - - [Fact] - public async Task AddTextAsync_Should_Write_The_Correct_Content_as_UTF8() - { - var someGoodUtf8Content = "some good utf8 content!"; - - var cid = await _dfs.AddTextAsync(someGoodUtf8Content); - - await _fileSystem.File.Received(1).WriteAllTextAsync( - Arg.Any(), - Arg.Is(b => b.Equals(someGoodUtf8Content)), - Arg.Is(Encoding.UTF8), - Arg.Any()); - - var utf8Hash = _hashProvider.ComputeUtf8MultiHash(someGoodUtf8Content); - var uf32Hash = _hashProvider.ComputeMultiHash(Encoding.UTF32.GetBytes(someGoodUtf8Content)); - - cid.Hash.Should().Be(utf8Hash); - cid.Hash.Should().NotBe(uf32Hash); - } - - [Fact] - public void Constructor_Should_Throw_On_Hash_Default_Size_Above_159() - { - HashingAlgorithm.Register("TooLong", 0x9999, 160); - - var toLongHashingAlgorithm = HashingAlgorithm.All.First(x => x.DigestSize > 159); - var longEnoughHashingAlgorithm = HashingAlgorithm.All.First(x => x.DigestSize <= 159); - - // ReSharper disable once ObjectCreationAsStatement - new Action(() => new DevDfs(_fileSystem, new HashProvider(toLongHashingAlgorithm))).Should() - .Throw() - .And.Message.Should().Contain(nameof(HashingAlgorithm)); - - // ReSharper disable once ObjectCreationAsStatement - new Action(() => new DevDfs(_fileSystem, new HashProvider(longEnoughHashingAlgorithm))).Should() - .NotThrow(); - } - - [Fact] - public async Task ReadAsync_Should_Point_To_The_Correct_File() - { - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("file")); - await _dfs.ReadAsync(cid); - _fileSystem.File.Received(1) - .OpenRead(Arg.Is(s => s.Equals(Path.Combine(_baseFolder, cid.Encode())))); - } - - [Fact] - public async Task ReadTextAsync_Be_Cancellable() - { - var cancellationToken = new CancellationToken(); - await _dfs.ReadTextAsync( - _hashProvider.ComputeUtf8MultiHash(@"https://media.giphy.com/media/KZwQMLTSx7M8bJ9OkZ/giphy.gif"), - cancellationToken); - await _fileSystem.File.Received(1).ReadAllTextAsync( - Arg.Any(), - Arg.Any(), - Arg.Is(cancellationToken)); - } - - [Fact] - public async Task ReadTextAsync_Should_Assume_UTF8_Content() - { - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("file")); - await _dfs.ReadTextAsync(cid); - await _fileSystem.File.Received(1).ReadAllTextAsync( - Arg.Any(), - Arg.Is(Encoding.UTF8), - Arg.Any()); - } - - [Fact] - public async Task ReadTextAsync_Should_Point_To_The_Correct_File() - { - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("file")); - await _dfs.ReadTextAsync(cid); - await _fileSystem.File.Received(1).ReadAllTextAsync( - Arg.Is(s => s.Equals(Path.Combine(_baseFolder, cid.Encode()))), - Arg.Any(), - Arg.Any()); - } - } -} diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/star_trails.mp4 b/src/Catalyst.Core.Modules.Dfs.Tests/star_trails.mp4 new file mode 100644 index 0000000000..a12b7a1a54 Binary files /dev/null and b/src/Catalyst.Core.Modules.Dfs.Tests/star_trails.mp4 differ diff --git a/src/Catalyst.Core.Modules.Dfs.Tests/starx2.mp4 b/src/Catalyst.Core.Modules.Dfs.Tests/starx2.mp4 new file mode 100644 index 0000000000..101c3506eb Binary files /dev/null and b/src/Catalyst.Core.Modules.Dfs.Tests/starx2.mp4 differ diff --git a/src/Catalyst.Core.Modules.Dfs/BlockExchange/BitSwapService.cs b/src/Catalyst.Core.Modules.Dfs/BlockExchange/BitSwapService.cs new file mode 100644 index 0000000000..0b0adaa5b6 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/BlockExchange/BitSwapService.cs @@ -0,0 +1,559 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.BlockExchange; +using Catalyst.Abstractions.Dfs.BlockExchange.Protocols; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Core.Modules.Dfs.BlockExchange.Protocols; +using Common.Logging; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs.BlockExchange +{ + /// + /// Exchange blocks with other peers. + /// + public class BitSwapService : IBitswapService + { + private static ILog _log = LogManager.GetLogger(typeof(BitSwapService)); + + private readonly ConcurrentDictionary _wants = new(); + private readonly ConcurrentDictionary _peerLedgers = new(); + + /// + /// The supported bitswap protocols. + /// + /// + /// Defaults to and + /// Bitswap1 + /// + /// . + /// + public IBitswapProtocol[] Protocols { get; set; } + + /// + /// The number of blocks sent by other peers. + /// + private ulong _blocksReceived; + + /// + /// The number of bytes sent by other peers. + /// + private ulong _dataReceived; + + /// + /// The number of blocks sent to other peers. + /// + private ulong _blocksSent; + + /// + /// The number of bytes sent to other peers. + /// + private ulong _dataSent; + + /// + /// The number of duplicate blocks sent by other peers. + /// + /// + /// A duplicate block is a block that is already stored in the + /// local repository. + /// + private ulong _dupBlksReceived; + + /// + /// The number of duplicate bytes sent by other peers. + /// + /// + /// A duplicate block is a block that is already stored in the + /// local repository. + /// + private ulong _dupDataReceived; + + /// + /// Creates a new instance of the class. + /// + public BitSwapService(ISwarmService swarmService) + { + SwarmService = swarmService; + + Protocols = new IBitswapProtocol[] + { + new Bitswap11 {BitswapService = this} + }; + } + + /// + /// Provides access to other peers. + /// + public ISwarmService SwarmService { get; set; } + + /// + /// Provides access to blocks of data. + /// + public IBlockApi BlockService { get; set; } + + /// + /// Statistics on the bitswap component. + /// + /// + public BitswapData Statistics + { + get + { + return new BitswapData + { + BlocksReceived = _blocksReceived, + BlocksSent = _blocksSent, + DataReceived = _dataReceived, + DataSent = _dataSent, + DupBlksReceived = _dupBlksReceived, + DupDataReceived = _dupDataReceived, + ProvideBufLen = 0, // TODO: Unknown meaning + Peers = SwarmService.KnownPeers.Select(p => p.Id), + Wantlist = _wants.Keys + }; + } + } + + /// + /// Gets the bitswap ledger for the specified peer. + /// + /// + /// The peer to get information on. If the peer is unknown, then a ledger + /// with zeros is returned. + /// + /// + /// Statistics on the bitswap blocks exchanged with the peer. + /// + /// + public BitswapLedger PeerLedger(Peer peer) + { + return _peerLedgers.TryGetValue(peer, out var ledger) ? ledger : new BitswapLedger {Peer = peer}; + } + + /// + /// Raised when a blocked is needed. + /// + /// + /// Only raised when a block is first requested. + /// + public event EventHandler BlockNeeded; + + /// + public Task StartAsync() + { + _log.Debug("Starting"); + + foreach (var protocol in Protocols) + { + SwarmService.AddProtocol(protocol); + } + + SwarmService.ConnectionEstablished += Swarm_ConnectionEstablished; + + // TODO: clear the stats. + _peerLedgers.Clear(); + + return Task.CompletedTask; + } + + // When a connection is established + // (1) Send the local peer's want list to the remote +#pragma warning disable VSTHRD100 // Avoid async void methods + private async void Swarm_ConnectionEstablished(object sender, PeerConnection connection) +#pragma warning restore VSTHRD100 // Avoid async void methods + { + if (_wants.Count == 0) + { + return; + } + + try + { + // There is a race condition between getting the remote identity and + // the remote sending the first wantlist. + var peer = await connection.IdentityEstablished.Task.ConfigureAwait(false); + + // Fire and forget. + var _ = SendWantListAsync(peer, _wants.Values, true); + } + catch (Exception e) + { + _log.Warn("Sending want list", e); + } + } + + /// + public Task StopAsync() + { + _log.Debug("Stopping"); + + SwarmService.ConnectionEstablished -= Swarm_ConnectionEstablished; + foreach (var protocol in Protocols) + { + SwarmService.RemoveProtocol(protocol); + } + + foreach (var cid in _wants.Keys) + { + Unwant(cid); + } + + return Task.CompletedTask; + } + + /// + /// The blocks needed by the peer. + /// + /// + /// The unique ID of the peer. + /// + /// + /// The sequence of CIDs need by the . + /// + public IEnumerable PeerWants(MultiHash peer) + { + return _wants.Values + .Where(w => w.Peers.Contains(peer)) + .Select(w => w.Id); + } + + /// + /// Adds a block to the want list. + /// + /// + /// The CID of the block to add to the want list. + /// + /// + /// The unique ID of the peer that wants the block. This is for + /// information purposes only. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the contents of block. + /// + /// + /// Other peers are informed that the block is needed by this peer. Hopefully, + /// someone will forward it to us. + /// + /// Besides using for cancellation, the + /// method will also cancel the operation. + /// + /// + public Task WantAsync(Cid id, MultiHash peer, CancellationToken cancel) + { + if (_log.IsDebugEnabled) + { + _log.Debug($"{peer} wants {id}"); + } + + TaskCompletionSource tsc = new(); + var want = _wants.AddOrUpdate( + id, + (key) => new WantedBlock + { + Id = id, + Consumers = new List> {tsc}, + Peers = new List {peer} + }, + (key, block) => + { + block.Peers.Add(peer); + block.Consumers.Add(tsc); + return block; + } + ); + + // If cancelled, then the block is unwanted. + cancel.Register(() => Unwant(id)); + + // If first time, tell other peers. + if (want.Consumers.Count != 1) + { + return tsc.Task; + } + + var _ = SendWantListToAllAsync(new[] {want}, full: false); + BlockNeeded?.Invoke(this, new CidEventArgs {Id = want.Id}); + + return tsc.Task; + } + + /// + /// Removes the block from the want list. + /// + /// + /// The CID of the block to remove from the want list. + /// + /// + /// Any tasks waiting for the block are cancelled. + /// + /// No exception is thrown if the is not + /// on the want list. + /// + /// + public void Unwant(Cid id) + { + if (_log.IsDebugEnabled) + { + _log.Debug($"Unwant {id}"); + } + + if (!_wants.TryRemove(id, out var block)) + { + return; + } + + foreach (var consumer in block.Consumers) + { + consumer.SetCanceled(); + } + + // TODO: Tell the swarm + } + + /// + /// Indicate that a remote peer sent a block. + /// + /// + /// The peer that sent the block. + /// + /// + /// The data for the block. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// + /// Updates the statistics. + /// + /// + /// If the block is acceptable then the is added to local cache + /// via the . + /// + /// + public Task OnBlockReceivedAsync(Peer remote, byte[] block) + { + return OnBlockReceivedAsync(remote, block, Cid.DefaultContentType, MultiHash.DefaultAlgorithmName); + } + + /// + /// Indicate that a remote peer sent a block. + /// + /// + /// The peer that sent the block. + /// + /// + /// The data for the block. + /// + /// + /// The of the block. + /// + /// + /// The multihash algorithm name of the block. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// + /// Updates the statistics. + /// + /// + /// If the block is acceptable then the is added to local cache + /// via the . + /// + /// + public async Task OnBlockReceivedAsync(Peer remote, byte[] block, string contentType, string multiHash) + { + // Update statistics. + ++_blocksReceived; + _dataReceived += (ulong) block.LongLength; + _peerLedgers.AddOrUpdate(remote, + (peer) => new BitswapLedger + { + Peer = peer, + BlocksExchanged = 1, + DataReceived = (ulong) block.LongLength + }, + (peer, ledger) => + { + ++ledger.BlocksExchanged; + _dataReceived += (ulong) block.LongLength; + return ledger; + }); + + // TODO: Detect if duplicate and update stats + const bool isDuplicate = false; + if (isDuplicate) + { + ++_dupBlksReceived; + _dupDataReceived += (ulong) block.Length; + } + + // TODO: Determine if we should accept the block from the remote. + const bool acceptable = true; + if (acceptable) + { + await BlockService.PutAsync( + data: block, + contentType: contentType, + multiHash) + .ConfigureAwait(false); + } + } + + /// + /// Indicate that the local peer sent a block to a remote peer. + /// + /// + /// The peer that sent the block. + /// + /// + /// The data for the block. + /// + /// + /// A task that represents the asynchronous operation. + /// + public Task OnBlockSentAsync(Peer remote, IDataBlock block) + { + ++_blocksSent; + _dataSent += (ulong) block.Size; + _peerLedgers.AddOrUpdate(remote, + (peer) => new BitswapLedger + { + Peer = peer, + BlocksExchanged = 1, + DataSent = (ulong) block.Size + }, + (peer, ledger) => + { + ++ledger.BlocksExchanged; + _dataSent += (ulong) block.Size; + return ledger; + }); + + return Task.CompletedTask; + } + + /// + /// Indicate that a block is found. + /// + /// + /// The block that was found. + /// + /// + /// The number of consumers waiting for the . + /// + /// + /// Found should be called whenever a new block is discovered. + /// It will continue any Task that is waiting for the block and + /// remove the block from the want list. + /// + public int Found(IDataBlock block) + { + if (!_wants.TryRemove(block.Id, out WantedBlock want)) + { + return 0; + } + + foreach (var consumer in want.Consumers) + { + consumer.SetResult(block); + } + + return want.Consumers.Count; + } + + /// + /// Send our want list to the connected peers. + /// + private async Task SendWantListToAllAsync(IEnumerable wantedBlocks, bool full) + { + if (SwarmService == null) + { + return; + } + + try + { + var tasks = SwarmService.KnownPeers + .Where(p => p.ConnectedAddress != null) + .Select(p => SendWantListAsync(p, wantedBlocks, full)) + .ToArray(); + if (_log.IsDebugEnabled) + { + _log.Debug($"Spamming {tasks.Count()} connected peers"); + } + + await Task.WhenAll(tasks).ConfigureAwait(false); + + if (_log.IsDebugEnabled) + { + _log.Debug($"Spam {tasks.Count()} connected peers done"); + } + } + catch (Exception e) + { + _log.Debug("sending to all failed", e); + } + } + + private async Task SendWantListAsync(Peer peer, IEnumerable wants, bool full) + { + _log.Debug($"sending want list to {peer}"); + + // Send the want list to the peer on any bitswap protocol + // that it supports. + foreach (var protocol in Protocols) + { + try + { + await using (var stream = await SwarmService.DialAsync(peer, protocol.ToString()).ConfigureAwait(false)) + { + await protocol.SendWantsAsync(stream, wants, full: full).ConfigureAwait(false); + } + + return; + } + catch (Exception) + { + _log.Debug($"{peer} refused {protocol}"); + } + } + + _log.Warn($"{peer} does not support any bitswap protocol"); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/BlockExchange/Protocols/Bitswap11.cs b/src/Catalyst.Core.Modules.Dfs/BlockExchange/Protocols/Bitswap11.cs new file mode 100644 index 0000000000..c40313148f --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/BlockExchange/Protocols/Bitswap11.cs @@ -0,0 +1,273 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.BlockExchange; +using Catalyst.Abstractions.Dfs.BlockExchange.Protocols; +using Common.Logging; +using Lib.P2P; +using MultiFormats; +using ProtoBuf; +using Semver; +using ProtoBufHelper = Lib.P2P.ProtoBufHelper; + +#pragma warning disable 0649 // disable warning about unassigned fields +#pragma warning disable 0169 // disable warning about unassigned fields + +namespace Catalyst.Core.Modules.Dfs.BlockExchange.Protocols +{ + /// + /// Bitswap Protocol version 1.1.0 + /// + public class Bitswap11 : IBitswapProtocol + { + private static ILog log = LogManager.GetLogger(typeof(Bitswap11)); + + /// + public string Name { get; } = "ipfs/bitswap"; + + /// + public SemVersion Version { get; } = new(1, 1); + + /// + public override string ToString() { return $"/{Name}/{Version}"; } + + /// + /// The service. + /// + public IBitswapService BitswapService { get; set; } + + /// + public async Task ProcessMessageAsync(PeerConnection connection, + Stream stream, + CancellationToken cancel = default) + { + // There is a race condition between getting the remote identity and + // the remote sending the first wantlist. + await connection.IdentityEstablished.Task.ConfigureAwait(false); + + while (true) + { + var request = await ProtoBufHelper.ReadMessageAsync(stream, cancel).ConfigureAwait(false); + + // Process want list + if (request.wantlist != null && request.wantlist.entries != null) + { + foreach (var entry in request.wantlist.entries) + { + var cid = Cid.Read(entry.block); + if (entry.cancel) + { + // TODO: Unwant specific to remote peer + BitswapService.Unwant(cid); + } + else + { + // TODO: Should we have a timeout? + var _ = GetBlockAsync(cid, connection.RemotePeer, CancellationToken.None); + } + } + } + + // Forward sent blocks to the block service. Eventually + // bitswap will here about and them and then continue + // any tasks (GetBlockAsync) waiting for the block. + if (request.payload == null) + { + continue; + } + + log.Debug($"got block(s) from {connection.RemotePeer}"); + foreach (var sentBlock in request.payload) + { + await using (MemoryStream ms = new(sentBlock.prefix)) + { + ms.ReadVarint32(); + var contentType = ms.ReadMultiCodec().Name; + var multiHash = MultiHash.GetHashAlgorithmName(ms.ReadVarint32()); + await BitswapService.OnBlockReceivedAsync(connection.RemotePeer, sentBlock.data, contentType, + multiHash); + } + } + } + } + + private async Task GetBlockAsync(Cid cid, Peer remotePeer, CancellationToken cancel) + { + // TODO: Determine if we will fetch the block for the remote + try + { + IDataBlock block; + if (null != await BitswapService.BlockService.StatAsync(cid, cancel).ConfigureAwait(false)) + { + block = await BitswapService.BlockService.GetAsync(cid, cancel).ConfigureAwait(false); + } + else + { + block = await BitswapService.WantAsync(cid, remotePeer.Id, cancel).ConfigureAwait(false); + } + + // Send block to remote. + await using (var stream = await BitswapService.SwarmService.DialAsync(remotePeer, ToString(), cancel).ConfigureAwait(false)) + { + await SendAsync(stream, block, cancel).ConfigureAwait(false); + } + + await BitswapService.OnBlockSentAsync(remotePeer, block).ConfigureAwait(false); + } + catch (TaskCanceledException) + { + // eat it + } + catch (Exception e) + { + log.Warn("getting block for remote failed", e); + + // eat it. + } + } + + /// + public async Task SendWantsAsync(Stream stream, + IEnumerable wants, + bool full = true, + CancellationToken cancel = default) + { + var message = new Message + { + wantlist = new Wantlist + { + full = full, + entries = wants + .Select(w => + { + return new Entry + { + block = w.Id.ToArray() + }; + }) + .ToArray() + }, + payload = new List(0) + }; + + Serializer.SerializeWithLengthPrefix(stream, message, PrefixStyle.Base128); + await stream.FlushAsync(cancel).ConfigureAwait(false); + } + + private async Task SendAsync(Stream stream, + IDataBlock block, + CancellationToken cancel = default) + { + log.Debug($"Sending block {block.Id}"); + var message = new Message + { + payload = new List + { + new Block + { + prefix = GetCidPrefix(block.Id), + data = block.DataBytes + } + } + }; + + Serializer.SerializeWithLengthPrefix(stream, message, PrefixStyle.Base128); + await stream.FlushAsync(cancel).ConfigureAwait(false); + } + + /// + /// Gets the CID "prefix". + /// + /// + /// The CID. + /// + /// + /// A byte array of consisting of cid version, multicodec and multihash prefix (type + length). + /// + private byte[] GetCidPrefix(Cid id) + { + using (MemoryStream ms = new()) + { + ms.WriteVarint(id.Version); + ms.WriteMultiCodec(id.ContentType); + ms.WriteVarint(id.Hash.Algorithm.Code); + ms.WriteVarint(id.Hash.Digest.Length); + return ms.ToArray(); + } + } + + [ProtoContract] + private sealed class Entry + { + [ProtoMember(1)] + + // changed from string to bytes, it makes a difference in JavaScript + public byte[] block; // the block cid (cidV0 in bitswap 1.0.0, cidV1 in bitswap 1.1.0) + + [ProtoMember(2)] + public int priority = 1; // the priority (normalized). default to 1 + + [ProtoMember(3)] + public bool cancel; // whether this revokes an entry + } + + [ProtoContract] + private sealed class Wantlist + { + [ProtoMember(1)] + public Entry[] entries; // a list of wantlist entries + + [ProtoMember(2)] + public bool full; // whether this is the full wantlist. default to false + } + + [ProtoContract] + private sealed class Block + { + [ProtoMember(1)] + public byte[] prefix; // CID prefix (cid version, multicodec and multihash prefix (type + length) + + [ProtoMember(2)] + public byte[] data; + } + + [ProtoContract] + private sealed class Message + { + [ProtoMember(1)] + public Wantlist wantlist; + + [ProtoMember(2)] + public byte[][] blocks; // used to send Blocks in bitswap 1.0.0 + + [ProtoMember(3)] + public List payload; // used to send Blocks in bitswap 1.1.0 + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/Catalyst.Core.Modules.Dfs.csproj b/src/Catalyst.Core.Modules.Dfs/Catalyst.Core.Modules.Dfs.csproj index 0f4e51ac30..1e58b00685 100644 --- a/src/Catalyst.Core.Modules.Dfs/Catalyst.Core.Modules.Dfs.csproj +++ b/src/Catalyst.Core.Modules.Dfs/Catalyst.Core.Modules.Dfs.csproj @@ -1,27 +1,61 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Dfs - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Dfs.snk true + + 1701;1702;CS8002 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + - + - - + + + + diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/BitSwapApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/BitSwapApi.cs new file mode 100644 index 0000000000..dbd4ba92be --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/BitSwapApi.cs @@ -0,0 +1,83 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.BlockExchange; +using Catalyst.Abstractions.Dfs.CoreApi; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed class BitSwapApi : IBitSwapApi + { + private readonly IBitswapService _bitSwapService; + private readonly Peer _localPeer; + + public BitSwapApi(IBitswapService bitSwapService, Peer localPeer) + { + _bitSwapService = bitSwapService; + _localPeer = localPeer; + } + + public async Task GetAsync(Cid id, CancellationToken cancel = default) + { + var dataBlock = await _bitSwapService.WantAsync(id, _localPeer.Id, cancel).ConfigureAwait(false); + return dataBlock; + } + + public BitswapLedger GetBitSwapLedger(Peer peer, CancellationToken cancel = default) + { + try + { + return _bitSwapService.PeerLedger(peer); + } + catch (Exception) + { + // ignored + } + + return null; + } + + public int FoundBlock(IDataBlock block) { return _bitSwapService.Found(block); } + + public BitswapData GetBitSwapStatistics() { return _bitSwapService.Statistics; } + + public void UnWant(Cid id, CancellationToken cancel = default) { _bitSwapService.Unwant(id); } + + public async Task> WantsAsync(MultiHash peer = null, + CancellationToken cancel = default) + { + if (peer == null) + { + peer = _localPeer.Id; + } + + return _bitSwapService.PeerWants(peer); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/BlockApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/BlockApi.cs new file mode 100644 index 0000000000..0be802af5e --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/BlockApi.cs @@ -0,0 +1,396 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.Serialization; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.FileSystem; +using Common.Logging; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + [DataContract] + internal class DataBlock : IDataBlock + { + [DataMember] + public byte[] DataBytes { get; set; } + + public Stream DataStream => new MemoryStream(DataBytes, false); + + [DataMember] + public Cid Id { get; set; } + + [DataMember] + public long Size { get; set; } + } + + internal sealed class BlockApi : IBlockApi + { + private static readonly ILog Log = LogManager.GetLogger(typeof(BlockApi)); + + private static DataBlock GetEmptyDirectory() + { + var emptyDirectory = ObjectApi.GetEmptyDirectory(); + return new DataBlock + { + DataBytes = emptyDirectory.ToArray(), + Id = emptyDirectory.Id, + Size = emptyDirectory.ToArray().Length + }; + } + + private static DataBlock GetEmptyNode() + { + var emptyNode = ObjectApi.GetEmptyNode(); + return new DataBlock + { + DataBytes = emptyNode.ToArray(), + Id = emptyNode.Id, + Size = emptyNode.ToArray().Length + }; + } + + private FileStore _store; + + private readonly IDhtApi _dhtApi; + private readonly ISwarmApi _swarmApi; + private readonly IBitSwapApi _bitSwapApi; + private readonly DfsOptions _dfsOptions; + private readonly DfsState _dfsState; + + public IPinApi PinApi { get; set; } + + /// + /// + /// + /// + /// + /// + public BlockApi(IBitSwapApi bitSwapApi, + IDhtApi dhtApi, + ISwarmApi swarmApi, + DfsState dfsState, + DfsOptions dfsOptions) + { + _bitSwapApi = bitSwapApi; + _dhtApi = dhtApi; + _swarmApi = swarmApi; + _dfsOptions = dfsOptions; + _dfsState = dfsState; + } + + public FileStore Store + { + get + { + if (_store != null) + { + return _store; + } + + var folder = Path.Combine(_dfsOptions.Repository.Folder, "blocks"); + + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } + + _store = new FileStore + { + Folder = folder, + NameToKey = cid => cid.Hash.ToBase32(), + KeyToName = key => + new MultiHash(key.FromBase32()), // @TODO assumes base32 should get from HashLibProvider prop + + Serialize = async (stream, cid, block, cancel) => + { + await stream.WriteAsync(block.DataBytes, 0, block.DataBytes.Length, cancel) + .ConfigureAwait(false); + }, + + Deserialize = async (stream, cid, cancel) => + { + var block = new DataBlock + { + Id = cid, + Size = stream.Length + }; + + block.DataBytes = new byte[block.Size]; + + for (int i = 0, n; i < block.Size; i += n) + { + n = await stream.ReadAsync(block.DataBytes, i, (int) block.Size - i, cancel) + .ConfigureAwait(false); + } + + return block; + } + }; + + return _store; + } + } + + public async Task GetAsync(Cid id, CancellationToken cancel = default) + { + // Hack for empty object and empty directory object + var emptyDirectory = GetEmptyDirectory(); + if (id == emptyDirectory.Id) + { + return emptyDirectory; + } + + var emptyNode = GetEmptyNode(); + if (id == emptyNode.Id) + { + return emptyNode; + } + + // If identity hash, then CID has the content. + if (id.Hash.IsIdentityHash) + { + return new DataBlock + { + DataBytes = id.Hash.Digest, + Id = id, + Size = id.Hash.Digest.Length + }; + } + + // Check the local filesystem for the block. + var block = await Store.TryGetAsync(id, cancel).ConfigureAwait(false); + + if (block != null) + { + return block; + } + + // Query the network, via DHT, for peers that can provide the + // content. As a provider peer is found, it is connected to and + // the bitSwap want lists are exchanged. Hopefully the provider will + // then send the block to us via bitSwap and the get task will finish. + using (var queryCancel = CancellationTokenSource.CreateLinkedTokenSource(cancel)) + { + var bitswapGet = await _bitSwapApi.GetAsync(id, queryCancel.Token).ConfigureAwait(false); + var _ = _dhtApi.FindProvidersAsync( + id, + 20, // TODO: remove this + peer => + { + var __ = ProviderFoundAsync(peer, queryCancel.Token).ConfigureAwait(false); + }, + queryCancel.Token + ); + + //log.Debug("bitswap got the block"); + + queryCancel.Cancel(false); // stop the network query. + return bitswapGet; + } + + //using (var queryCancel = CancellationTokenSource.CreateLinkedTokenSource(cancel)) + //{ + // var bitSwapGet = _bitSwapApi.GetAsync(id, queryCancel.Token).ConfigureAwait(false); + + // var providers = await _dhtApi.FindProvidersAsync( + // id: id, + // cancel: queryCancel.Token + // ); + + // var enumerable = providers as Peer[] ?? providers.ToArray(); + // for (var index = 0; index < enumerable.ToArray().Length; index++) + // { + // var peer = enumerable.ToArray()[index]; + // var _ = ProviderFoundAsync(peer, queryCancel.Token).ConfigureAwait(false); + // } + + // var got = await bitSwapGet; + // Log.Debug("bitSwap got the block"); + + // queryCancel.Cancel(false); // stop the network query. + // return got; + //} + } + + private async Task ProviderFoundAsync(Peer peer, CancellationToken cancel) + { + if (cancel.IsCancellationRequested) + { + return; + } + + Log.Debug($"Connecting to provider {peer.Id}"); + + try + { + await _swarmApi.ConnectAsync(peer, cancel).ConfigureAwait(false); + } + catch (Exception e) + { + Log.Warn($"Connection to provider {peer.Id} failed, {e.Message}"); + } + } + + public async Task PutAsync(byte[] data, + string contentType = Cid.DefaultContentType, + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = false, + CancellationToken cancel = default) + { + if (data.Length > _dfsOptions.Block.MaxBlockSize) + { + throw new ArgumentOutOfRangeException($"data.Length", + $@"Block length can not exceed {_dfsOptions.Block.MaxBlockSize.ToString()}."); + } + + // Small enough for an inline CID? + if (_dfsOptions.Block.AllowInlineCid && data.Length <= _dfsOptions.Block.InlineCidLimit) + { + return new Cid + { + ContentType = contentType, + Hash = MultiHash.ComputeHash(data, "identity") + }; + } + + // CID V1 encoding defaulting to base32 which is not + var cid = new Cid + { + ContentType = contentType, + Hash = MultiHash.ComputeHash(data, multiHash) + }; + + if (encoding != "base58btc") + { + cid.Encoding = encoding; + } + + var block = new DataBlock + { + DataBytes = data, + Id = cid, + Size = data.Length + }; + + if (await Store.ExistsAsync(cid, cancel).ConfigureAwait(false)) + { + Log.DebugFormat("Block '{0}' already present", cid); + } + else + { + await Store.PutAsync(cid, block, cancel).ConfigureAwait(false); + if (_dfsState.IsStarted) + { + await _dhtApi.ProvideAsync(cid, false, cancel).ConfigureAwait(false); + } + + Log.DebugFormat("Added block '{0}'", cid); + } + + // Inform the BitSwap service. + _bitSwapApi.FoundBlock(block); + + // To pin or not. + if (pin) + { + await PinApi.AddAsync(cid, false, cancel).ConfigureAwait(false); + } + else + { + await PinApi.RemoveAsync(cid, false, cancel).ConfigureAwait(false); + } + + return cid; + } + + public async Task PutAsync(Stream data, + string contentType = Cid.DefaultContentType, + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = false, + CancellationToken cancel = default) + { + await using (MemoryStream ms = new()) + { + await data.CopyToAsync(ms, cancel).ConfigureAwait(false); + return await PutAsync(ms.ToArray(), contentType, multiHash, encoding, pin, cancel) + .ConfigureAwait(false); + } + } + + public async Task RemoveAsync(Cid id, + bool ignoreNonexistent = false, + CancellationToken cancel = default) + { + if (id.Hash.IsIdentityHash) + { + return id; + } + + if (await Store.ExistsAsync(id, cancel).ConfigureAwait(false)) + { + await Store.RemoveAsync(id, cancel).ConfigureAwait(false); + await PinApi.RemoveAsync(id, false, cancel).ConfigureAwait(false); + return id; + } + + if (ignoreNonexistent) + { + return null; + } + + throw new KeyNotFoundException($"Block '{id.Encode()}' does not exist."); + } + + public async Task StatAsync(Cid id, CancellationToken cancel = default) + { + if (id.Hash.IsIdentityHash) + { + return await GetAsync(id, cancel).ConfigureAwait(false); + } + + IDataBlock block = null; + var length = await Store.LengthAsync(id, cancel).ConfigureAwait(false); + + if (length.HasValue) + { + block = new DataBlock + { + Id = id, + Size = length.Value + }; + } + + return block; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/BlockRepositoryApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/BlockRepositoryApi.cs new file mode 100644 index 0000000000..8efea045b3 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/BlockRepositoryApi.cs @@ -0,0 +1,104 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Globalization; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Dfs.Migration; +using Catalyst.Abstractions.Options; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed class BlockRepositoryApi : IBlockRepositoryApi + { + private IPinApi _pinApi; + private IBlockApi _blockApi; + private readonly IMigrationManager _migrationManager; + private readonly RepositoryOptions _repositoryOptions; + + public BlockRepositoryApi(IPinApi pinApi, IBlockApi blockApi, IMigrationManager migrationManager, RepositoryOptions repositoryOptions) + { + _pinApi = pinApi; + _blockApi = blockApi; + _migrationManager = migrationManager; + _repositoryOptions = repositoryOptions; + } + + public async Task RemoveGarbageAsync(CancellationToken cancel = default) + { + var blockApi = (BlockApi) _blockApi; + var pinApi = (PinApi) _pinApi; + foreach (var cid in blockApi.Store.Names) + { + if (!await pinApi.IsPinnedAsync(cid, cancel).ConfigureAwait(false)) + { + await _blockApi.RemoveAsync(cid, ignoreNonexistent: true, cancel: cancel).ConfigureAwait(false); + } + } + } + + public async Task StatisticsAsync(CancellationToken cancel = default) + { + var data = new RepositoryData + { + RepoPath = Path.GetFullPath(_repositoryOptions.Folder), + Version = await VersionAsync(cancel).ConfigureAwait(false), + StorageMax = 10000000000 // TODO: there is no storage max + }; + + var blockApi = (BlockApi) _blockApi; + GetDirStats(blockApi.Store.Folder, data, cancel); + + return data; + } + + public Task VerifyAsync(CancellationToken cancel = default) + { + throw new NotImplementedException(); + } + + public Task VersionAsync(CancellationToken cancel = default) + { + return Task.FromResult(_migrationManager.CurrentVersion.ToString(CultureInfo.InvariantCulture)); + } + + private void GetDirStats(string path, RepositoryData data, CancellationToken cancel) + { + foreach (var file in Directory.EnumerateFiles(path)) + { + cancel.ThrowIfCancellationRequested(); + ++data.NumObjects; + data.RepoSize += (ulong) (new FileInfo(file).Length); + } + + foreach (var dir in Directory.EnumerateDirectories(path)) + { + cancel.ThrowIfCancellationRequested(); + GetDirStats(dir, data, cancel); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/BootstrapApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/BootstrapApi.cs new file mode 100644 index 0000000000..fb29a1de22 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/BootstrapApi.cs @@ -0,0 +1,131 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Options; +using MultiFormats; +using Newtonsoft.Json.Linq; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + public class BootstrapApi : IBootstrapApi + { + // From https://github.com/libp2p/go-libp2p-daemon/blob/master/bootstrap.go#L14 + public static MultiAddress[] Defaults = + { + "/ip4/77.68.110.194/tcp/4001/ipfs/QmX3Ye8zfH1u46pyzWAeJjCLbH4XTVA89LGYK6fZsPFAkw", + "/ip4/77.68.110.195/tcp/4001/ipfs/QmVgGUKQw9FFX5iqKK2ZxHQtSfpDNiSiwUfykWrLQkBagK", + "/ip4/77.68.110.196/tcp/4001/ipfs/QmU9yjJLChQucWkjAiKD5HkMCoPdoo3ndP1kkkDqWUKeDN", + "/ip4/77.68.110.197/tcp/4001/ipfs/QmZf3ARncMmSZoNm5QZm4QXPsTjLJpUDKHsEQEhoPTwzuf" + + //"/ip4/192.168.1.45/tcp/4001/ipfs/QmUUNAUD5YLrCZ4vBn8WsxTbMjtgJYkAkAYiFyoEdb3edu", + //"/ip4/192.168.1.46/tcp/4001/ipfs/QmNNdTXfLRqo4Puc6JPGq2o3xDBcJhxDXCbThuzDV6nRP1", + //"/ip4/192.168.1.47/tcp/4001/ipfs/QmQVoUpHf3yveqcrF2cFTWgutrSFo1Cm1CSffqKQZ52WHL", + //"/ip4/192.168.1.233/tcp/4001/ipfs/QmPDpc3KHGtomZjAsfuw7ZaYrahVNBtXX8Kjoy6cCMRLp7", + //"/ip4/192.168.1.40/tcp/4001/ipfs/QmbRNTx28U6Wptthtog4vwXF5QtMyTfZdEX56MCFFPdZHB", + //"/ip4/192.168.1.232/tcp/4001/ipfs/QmaZtpXfM713jTpLACJ2njMm7Qi4D2FNrAjZKM1e6L9bLM" + }; + + private readonly IConfigApi _configApi; + private readonly DiscoveryOptions _discoveryOptions; + + public BootstrapApi(IConfigApi configApi, DiscoveryOptions discoveryOptions) + { + _configApi = configApi; + _discoveryOptions = discoveryOptions; + } + + public async Task AddAsync(MultiAddress address, + CancellationToken cancel = default) + { + // Throw if missing peer ID + var _ = address.PeerId; + + var addrs = (await ListAsync(cancel).ConfigureAwait(false)).ToList(); + if (addrs.Any(a => a == address)) + { + return address; + } + + addrs.Add(address); + var strings = addrs.Select(a => a.ToString()); + await _configApi.SetAsync("Bootstrap", JToken.FromObject(strings), cancel).ConfigureAwait(false); + return address; + } + + public async Task> AddDefaultsAsync(CancellationToken cancel = + default) + { + foreach (var a in Defaults) + { + await AddAsync(a, cancel).ConfigureAwait(false); + } + + return Defaults; + } + + public async Task> ListAsync(CancellationToken cancel = default) + { + if (_discoveryOptions.BootstrapPeers != null) + { + return _discoveryOptions.BootstrapPeers; + } + + try + { + var json = await _configApi.GetAsync("Bootstrap", cancel); + return json == null ? new MultiAddress[0] : json.Select(a => MultiAddress.TryCreate((string)a)).Where(a => a != null); + } + catch (KeyNotFoundException) + { + var strings = Defaults.Select(a => a.ToString()); + await _configApi.SetAsync("Bootstrap", JToken.FromObject(strings), cancel).ConfigureAwait(false); + return Defaults; + } + } + + public async Task RemoveAllAsync(CancellationToken cancel = default) + { + await _configApi.SetAsync("Bootstrap", JToken.FromObject(new string[0]), cancel).ConfigureAwait(false); + } + + public async Task RemoveAsync(MultiAddress address, + CancellationToken cancel = default) + { + var addrs = (await ListAsync(cancel).ConfigureAwait(false)).ToList(); + if (addrs.All(a => a != address)) + { + return address; + } + + addrs.Remove(address); + var strings = addrs.Select(a => a.ToString()); + await _configApi.SetAsync("Bootstrap", JToken.FromObject(strings), cancel).ConfigureAwait(false); + return address; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/DagApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/DagApi.cs new file mode 100644 index 0000000000..d5d973cc03 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/DagApi.cs @@ -0,0 +1,178 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Core.Modules.Dfs.LinkedData; +using Lib.P2P; +using MultiFormats; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; +using PeterO.Cbor; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed class DagApi : IDagApi + { + private static readonly PODOptions PodOptions = new PODOptions + ( + removeIsPrefix: false, + useCamelCase: false + ); + + private readonly IBlockApi _blockApi; + + public DagApi(IBlockApi blockApi) + { + _blockApi = blockApi; + } + + public async Task GetAsync(Cid id, + CancellationToken cancel = default) + { + var block = await _blockApi.GetAsync(id, cancel).ConfigureAwait(false); + var format = GetDataFormat(id); + var canonical = format.Deserialise(block.DataBytes); + await using (MemoryStream ms = new()) + using (StreamReader sr = new(ms)) + using (JsonTextReader reader = new(sr)) + { + canonical.WriteJSONTo(ms); + ms.Position = 0; + return (JObject) JToken.ReadFrom(reader); + } + } + + public async Task GetAsync(string path, + CancellationToken cancel = default) + { + if (path.StartsWith("/ipfs/")) + { + path = path.Remove(0, 6); + } + + var parts = path.Split('/').Where(p => p.Length > 0).ToArray(); + if (parts.Length == 0) + { + throw new ArgumentException($"Cannot resolve '{path}'."); + } + + JToken token = await GetAsync(Cid.Decode(parts[0]), cancel).ConfigureAwait(false); + foreach (var child in parts.Skip(1)) + { + token = ((JObject) token)[child]; + if (token == null) + { + throw new Exception($"Missing component '{child}'."); + } + } + + return token; + } + + public async Task GetAsync(Cid id, + CancellationToken cancel = default) + { + var block = await _blockApi.GetAsync(id, cancel).ConfigureAwait(false); + var format = GetDataFormat(id); + var canonical = format.Deserialise(block.DataBytes); + + // CBOR does not support serialisation to another Type + // see https://github.com/peteroupc/CBOR/issues/12. + // So, convert to JSON and use Newtonsoft to deserialise. + return JObject.Parse(canonical.ToJSONString()).ToObject(); + } + + public async Task PutAsync(JObject data, + string contentType = "dag-cbor", + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = true, + CancellationToken cancel = default) + { + await using (MemoryStream ms = new()) + await using (StreamWriter sw = new(ms)) + using (JsonTextWriter writer = new(sw)) + { + await data.WriteToAsync(writer, cancel); + writer.Flush(); + ms.Position = 0; + var format = GetDataFormat(contentType); + var block = format.Serialize(CBORObject.ReadJSON(ms)); + return await _blockApi.PutAsync(block, contentType, multiHash, encoding, pin, cancel) + .ConfigureAwait(false); + } + } + + public async Task PutAsync(Stream data, + string contentType = "dag-cbor", + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = true, + CancellationToken cancel = default) + { + var format = GetDataFormat(contentType); + var block = format.Serialize(CBORObject.Read(data)); + return await _blockApi.PutAsync(block, contentType, multiHash, encoding, pin, cancel) + .ConfigureAwait(false); + } + + public async Task PutAsync(object data, + string contentType = "dag-cbor", + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = true, + CancellationToken cancel = default) + { + var format = GetDataFormat(contentType); + var block = format.Serialize(CBORObject.FromObject(data, PodOptions)); + return await _blockApi.PutAsync(block, contentType, multiHash, encoding, pin, cancel) + .ConfigureAwait(false); + } + + private ILinkedDataFormat GetDataFormat(Cid id) + { + if (IpldRegistry.Formats.TryGetValue(id.ContentType, out var format)) + { + return format; + } + + throw new KeyNotFoundException($"Unknown IPLD format '{id.ContentType}'."); + } + + private ILinkedDataFormat GetDataFormat(string contentType) + { + if (IpldRegistry.Formats.TryGetValue(contentType, out var format)) + { + return format; + } + + throw new KeyNotFoundException($"Unknown IPLD format '{contentType}'."); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/DhtApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/DhtApi.cs new file mode 100644 index 0000000000..1b61367f05 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/DhtApi.cs @@ -0,0 +1,81 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Lib.P2P; +using Lib.P2P.Routing; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + public sealed class DhtApi : IDhtApi + { + private readonly IDhtService _dhtService; + + public DhtApi(IDhtService dhtService) + { + _dhtService = dhtService; + } + + public async Task FindPeerAsync(MultiHash id, CancellationToken cancel = default) + { + return await _dhtService.FindPeerAsync(id, cancel).ConfigureAwait(false); + } + + public async Task> FindProvidersAsync(Cid id, + int limit = 20, + Action providerFound = null, + CancellationToken cancel = default) + { + return await _dhtService.FindProvidersAsync(id, limit, providerFound, cancel).ConfigureAwait(false); + } + + public async Task ProvideAsync(Cid cid, + bool advertise = true, + CancellationToken cancel = default) + { + await _dhtService.ProvideAsync(cid, advertise, cancel).ConfigureAwait(false); + } + + public Task GetAsync(byte[] key, CancellationToken cancel = default) + { + throw new NotImplementedException(); + } + + public Task PutAsync(byte[] key, out byte[] value, CancellationToken cancel = default) + { + throw new NotImplementedException(); + } + + public Task TryGetAsync(byte[] key, + out byte[] value, + CancellationToken cancel = default) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/DnsApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/DnsApi.cs new file mode 100644 index 0000000000..ff9b684486 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/DnsApi.cs @@ -0,0 +1,160 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Lib.P2P; +using Makaretu.Dns; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed class DnsApi : IDnsApi + { + private readonly IDnsClient _dnsClient; + + public DnsApi(IDnsClient dnsClient) { _dnsClient = dnsClient; } + + public async Task + ResolveNameAsync(string name, + bool recursive = false, + bool nocache = false, + CancellationToken cancel = default) + { + do + { + if (name.StartsWith("/ipns/")) + { + name = name.Substring(6); + } + + var parts = name.Split('/').Where(p => p.Length > 0).ToArray(); + if (parts.Length == 0) + throw new ArgumentException($"Cannot resolve '{name}'."); + if (IsDomainName(parts[0])) + { + name = await ResolveAsync(parts[0], recursive, cancel).ConfigureAwait(false); + } + else + { + throw new NotImplementedException("Resolving IPNS is not yet implemented."); + } + + if (parts.Length > 1) + { + name = name + "/" + string.Join("/", parts, 1, parts.Length - 1); + } + } while (recursive && !name.StartsWith("/ipfs/")); + + return name; + } + + public async Task ResolveAsync(string name, bool recursive = false, CancellationToken cancel = default) + { + // Find the TXT dnslink in either or _dnslink.. + string link = null; + using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel)) + { + try + { + var attempts = new[] + { + FindAsync(name, cts.Token), + FindAsync("_dnslink." + name, cts.Token) + }; + link = await TaskHelper.WhenAnyResultAsync(attempts, cancel).ConfigureAwait(false); + cts.Cancel(); + } + catch (Exception e) + { + throw new NotSupportedException($"Cannot resolve '{name}'.", e); + } + } + + if (!recursive || link.StartsWith("/ipfs/")) + return link; + + if (link.StartsWith("/ipns/")) + { + return await ResolveNameAsync(link, recursive, false, cancel).ConfigureAwait(false); + } + + throw new NotSupportedException($"Cannot resolve '{link}'."); + } + + //public async Task ResolveAsync(string name, + // bool recursive = false, + // CancellationToken cancel = default(CancellationToken)) + //{ + // // Find the TXT dnslink in either or _dnslink.. + // string link = null; + // using (var cts = CancellationTokenSource.CreateLinkedTokenSource(cancel)) + // { + // try + // { + // var attempts = new Task[] + // { + // FindAsync(name, cts.Token), + // FindAsync("_dnslink." + name, cts.Token) + // }; + // link = await TaskHelper.WhenAnyResultAsync(attempts, cancel).ConfigureAwait(false); + // cts.Cancel(); + // } + // catch (Exception e) + // { + // throw new NotSupportedException($"Cannot resolve '{name}'.", e); + // } + // } + + // if (!recursive || link.StartsWith("/ipfs/")) + // { + // return link; + // } + + // // if (link.StartsWith("/ipns/")) + // // { + // // return await _nameApi.ResolveAsync(link, recursive, false, cancel).ConfigureAwait(false); + // // } + + // throw new NotSupportedException($"Cannot resolve '{link}'."); + //} + + private async Task FindAsync(string name, CancellationToken cancel) + { + var response = await _dnsClient.QueryAsync(name, DnsType.TXT, cancel).ConfigureAwait(false); + var link = response.Answers.OfType().SelectMany(txt => txt.Strings) + .Where(s => s.StartsWith("dnslink=")).Select(s => s.Substring(8)).FirstOrDefault(); + + if (link == null) + { + throw new Exception($"'{name}' is missing a TXT record with a dnslink."); + } + + return link; + } + + private static bool IsDomainName(string name) { return name.IndexOf('.') > 0; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/HashOnlyBlockService.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/HashOnlyBlockService.cs new file mode 100644 index 0000000000..b0abfe2a1d --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/HashOnlyBlockService.cs @@ -0,0 +1,88 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed partial class UnixFsApi + { + /// + /// A Block service that only computes the block's hash. + /// + private sealed class HashOnlyBlockService : IBlockApi + { + public IPinApi PinApi { get; set; } + + public Task PutAsync(byte[] data, + string contentType = Cid.DefaultContentType, + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = false, + CancellationToken cancel = default) + { + var cid = new Cid + { + ContentType = contentType, + Encoding = encoding, + Hash = MultiHash.ComputeHash(data, multiHash), + Version = (contentType == "dag-pb" && multiHash == "sha2-256") ? 0 : 1 + }; + return Task.FromResult(cid); + } + + public Task GetAsync(Cid id, CancellationToken cancel = default) + { + throw new NotImplementedException(); + } + + public Task PutAsync(Stream data, + string contentType = Cid.DefaultContentType, + string multiHash = MultiHash.DefaultAlgorithmName, + string encoding = MultiBase.DefaultAlgorithmName, + bool pin = false, + CancellationToken cancel = default) + { + throw new NotImplementedException(); + } + + public Task RemoveAsync(Cid id, + bool ignoreNonexistent = false, + CancellationToken cancel = default) + { + throw new NotImplementedException(); + } + + public Task StatAsync(Cid id, CancellationToken cancel = default) + { + throw new NotImplementedException(); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/NameApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/NameApi.cs new file mode 100644 index 0000000000..baaf8a1ea2 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/NameApi.cs @@ -0,0 +1,111 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.CoreApi; +using Lib.P2P; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed class NameApi : INameApi + { + private readonly IDnsApi _dnsApi; + private readonly IObjectApi _objectApi; + + public NameApi(IDnsApi dnsApi, IObjectApi objectApi) + { + _dnsApi = dnsApi; + _objectApi = objectApi; + } + + public Task PublishAsync(string path, + bool resolve = true, + string key = "self", + TimeSpan? lifetime = null, + CancellationToken cancel = default) + { + throw new NotImplementedException(); + } + + public Task PublishAsync(Cid id, + string key = "self", + TimeSpan? lifetime = null, + CancellationToken cancel = default) + { + throw new NotImplementedException(); + } + + public async Task ResolveAsync(string name, + bool recursive = false, + bool nocache = false, + CancellationToken cancel = default) + { + var path = name; + var parts = path.Split('/').Where(p => p.Length > 0).ToArray(); + if (path.StartsWith("/ipns/") || IsDomainName(parts[0])) + { + return await _dnsApi.ResolveNameAsync(path, recursive, false, cancel).ConfigureAwait(false); + } + + if (path.StartsWith("/ipfs/")) + { + path = path.Remove(0, 6); + } + + parts = path.Split('/').Where(p => p.Length > 0).ToArray(); + if (parts.Length == 0) + throw new ArgumentException($"Cannot resolve '{name}'."); + + var id = Cid.Decode(parts[0]); + foreach (var child in parts.Skip(1)) + { + var container = await _objectApi.GetAsync(id, cancel).ConfigureAwait(false); + var link = container.Links.FirstOrDefault(l => l.Name == child); + if (link == null) + throw new ArgumentException($"Cannot resolve '{child}' in '{name}'."); + id = link.Id; + } + + return "/ipfs/" + id.Encode(); + } + + /// + /// Determines if the supplied string is a valid domain name. + /// + /// + /// An domain name, such as "ipfs.io". + /// + /// + /// true if is a domain name; + /// otherwise, false. + /// + /// + /// A domain must contain at least one '.'. + /// + private static bool IsDomainName(string name) { return name.IndexOf('.') > 0; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/ObjectApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/ObjectApi.cs new file mode 100644 index 0000000000..888db9d1bc --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/ObjectApi.cs @@ -0,0 +1,134 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Core.Lib.Dag; +using Catalyst.Core.Modules.Dfs.UnixFs; +using Lib.P2P; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed class ObjectApi : IObjectApi + { + internal static IDagNode GetEmptyNode() + { + return new DagNode(new byte[0]); + } + + internal static IDagNode GetEmptyDirectory() + { + var dm = new DataMessage + { + Type = DataType.Directory + }; + using MemoryStream pb = new(); + ProtoBuf.Serializer.Serialize(pb, dm); + return new DagNode(pb.ToArray()); + } + + private readonly IBlockApi _blockApi; + + public ObjectApi(IBlockApi blockApi) + { + _blockApi = blockApi; + } + + public async Task DataAsync(Cid id, CancellationToken cancel = default) + { + var node = await GetAsync(id, cancel).ConfigureAwait(false); + return node.DataStream; + } + + public async Task GetAsync(Cid id, CancellationToken cancel = default) + { + var block = await _blockApi.GetAsync(id, cancel).ConfigureAwait(false); + return new DagNode(block.DataStream); + } + + public async Task> LinksAsync(Cid id, + CancellationToken cancel = default) + { + if (id.ContentType != "dag-pb") + { + return Enumerable.Empty(); + } + + var block = await _blockApi.GetAsync(id, cancel).ConfigureAwait(false); + DagNode node = new(block.DataStream); + return node.Links; + } + + public Task NewAsync(string template = null, CancellationToken cancel = default) + { + switch (template) + { + case null: + return Task.FromResult(GetEmptyNode()); + case "unixfs-dir": + return Task.FromResult(GetEmptyDirectory()); + default: + throw new ArgumentException($"Unknown template '{template}'.", nameof(template)); + } + } + + public Task NewDirectoryAsync(CancellationToken cancel = default) + { + return Task.FromResult(GetEmptyDirectory()); + } + + public Task PutAsync(byte[] data, + IEnumerable links = null, + CancellationToken cancel = default) + { + DagNode node = new(data, links); + return PutAsync(node, cancel); + } + + public async Task PutAsync(IDagNode node, CancellationToken cancel = default) + { + node.Id = await _blockApi.PutAsync(node.ToArray(), cancel: cancel).ConfigureAwait(false); + return node; + } + + public async Task StatAsync(Cid id, CancellationToken cancel = default) + { + var block = await _blockApi.GetAsync(id, cancel).ConfigureAwait(false); + DagNode node = new(block.DataStream); + return new ObjectStat + { + BlockSize = block.Size, + DataSize = node.DataBytes.Length, + LinkCount = node.Links.Count(), + LinkSize = block.Size - node.DataBytes.Length, + CumulativeSize = block.Size + node.Links.Sum(link => link.Size) + }; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/PinApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/PinApi.cs new file mode 100644 index 0000000000..384b19bb1c --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/PinApi.cs @@ -0,0 +1,178 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.FileSystem; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed class Pin + { + public Cid Id; + } + + internal sealed class PinApi : IPinApi + { + private FileStore _store; + + private readonly INameApi _nameApi; + public IBlockApi BlockApi { get; set; } + private readonly IObjectApi _objectApi; + private readonly RepositoryOptions _repositoryOptions; + + public PinApi(INameApi nameApi, + IObjectApi objectApi, + RepositoryOptions options) + { + _nameApi = nameApi; + _objectApi = objectApi; + _repositoryOptions = options; + } + + private FileStore Store + { + get + { + if (_store != null) + { + return _store; + } + + var folder = Path.Combine(_repositoryOptions.Folder, "pins"); + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } + + _store = new FileStore + { + Folder = folder, + NameToKey = cid => cid.Hash.ToBase32(), + KeyToName = key => new MultiHash(key.FromBase32()) + }; + + return _store; + } + } + + public async Task> AddAsync(string path, + bool recursive = true, + CancellationToken cancel = default) + { + var r = await _nameApi.ResolveAsync(path, cancel: cancel).ConfigureAwait(false); + var id = Cid.Decode(r.Remove(0, 6)); + Stack todos = new(); + todos.Push(id); + List dones = new(); + + // The pin is added before the content is fetched, so that + // garbage collection will not delete the newly pinned + // content. + + while (todos.Count > 0) + { + var current = todos.Pop(); + + // Add CID to PIN database. + await Store.PutAsync(current, new Pin + { + Id = current + }, cancel).ConfigureAwait(false); + + // Make sure that the content is stored locally. + await BlockApi.GetAsync(current, cancel).ConfigureAwait(false); + + // Recursively pin the links? + if (recursive && current.ContentType == "dag-pb") + { + var links = await _objectApi.LinksAsync(current, cancel); + foreach (var link in links) + { + todos.Push(link.Id); + } + } + + dones.Add(current); + } + + return dones; + } + + public Task> ListAsync(CancellationToken cancel = default) + { + var cids = Store.Values.Select(pin => pin.Id); + return Task.FromResult(cids); + } + + public async Task> RemoveAsync(Cid id, + bool recursive = true, + CancellationToken cancel = default) + { + Stack todos = new(); + todos.Push(id); + List dones = new(); + + while (todos.Count > 0) + { + var current = todos.Pop(); + await Store.RemoveAsync(current, cancel).ConfigureAwait(false); + if (recursive) + { + if (null != await BlockApi.StatAsync(current, cancel).ConfigureAwait(false)) + { + try + { + var links = await _objectApi.LinksAsync(current, cancel).ConfigureAwait(false); + foreach (var link in links) + { + todos.Push(link.Id); + } + } + catch (Exception) + { + // ignored + } + } + } + + dones.Add(current); + } + + return dones; + } + + public async Task IsPinnedAsync(Cid id, CancellationToken cancel = default) + { + return await Store.ExistsAsync(id, cancel).ConfigureAwait(false); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/PubSubApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/PubSubApi.cs new file mode 100644 index 0000000000..752fa335bf --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/PubSubApi.cs @@ -0,0 +1,84 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Lib.P2P; +using Lib.P2P.PubSub; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed class PubSubApi : IPubSubApi + { + private readonly IPubSubService _pubSubService; + + public PubSubApi(IPubSubService pubSubService) + { + _pubSubService = pubSubService; + } + + public async Task> PeersAsync(string topic = null, + CancellationToken cancel = default) + { + return await _pubSubService.PeersAsync(topic, cancel); + } + + public async Task PublishAsync(string topic, + string message, + CancellationToken cancel = default) + { + await _pubSubService.PublishAsync(topic, message, cancel); + } + + public async Task PublishAsync(string topic, + byte[] message, + CancellationToken cancel = default) + { + await _pubSubService.PublishAsync(topic, message, cancel); + } + + public async Task PublishAsync(string topic, + Stream message, + CancellationToken cancel = default) + { + await _pubSubService.PublishAsync(topic, message, cancel); + } + + public async Task SubscribeAsync(string topic, + Action handler, + CancellationToken cancellationToken) + { + await _pubSubService.SubscribeAsync(topic, handler, cancellationToken); + } + + public async Task> SubscribedTopicsAsync(CancellationToken cancel = + default) + { + return await _pubSubService.SubscribedTopicsAsync(cancel); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/StatsApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/StatsApi.cs new file mode 100644 index 0000000000..b6c2adaa53 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/StatsApi.cs @@ -0,0 +1,58 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Lib.P2P; +using Lib.P2P.Transports; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed class StatsApi : IStatsApi + { + private readonly IBitSwapApi _bitSwapApi; + private readonly IBlockRepositoryApi _blockRepositoryApi; + + public StatsApi(IBitSwapApi bitSwapApi, IBlockRepositoryApi blockRepositoryApi) + { + _bitSwapApi = bitSwapApi; + _blockRepositoryApi = blockRepositoryApi; + } + + public Task GetBandwidthStatsAsync(CancellationToken cancel = default) + { + return Task.FromResult(StatsStream.AllBandwidth); + } + + public BitswapData GetBitSwapStats(CancellationToken cancel = default) + { + return _bitSwapApi.GetBitSwapStatistics(); + } + + public Task GetRepositoryStatsAsync(CancellationToken cancel = default) + { + return _blockRepositoryApi.StatisticsAsync(cancel); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/SwarmApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/SwarmApi.cs new file mode 100644 index 0000000000..5b2590ece5 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/SwarmApi.cs @@ -0,0 +1,135 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Common.Logging; +using Lib.P2P; +using MultiFormats; +using Newtonsoft.Json.Linq; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed class SwarmApi : ISwarmApi + { + private static readonly ILog Log = LogManager.GetLogger(typeof(SwarmApi)); + private readonly ISwarmService _swarmService; + + private static readonly MultiAddress[] DefaultFilters = { }; + private readonly IConfigApi _configApi; + + public SwarmApi(ISwarmService swarmService, IConfigApi configApi) + { + _swarmService = swarmService; + _configApi = configApi; + } + + public async Task AddAddressFilterAsync(MultiAddress address, + bool persist = false, + CancellationToken cancel = default) + { + var addrs = (await ListAddressFiltersAsync(persist, cancel).ConfigureAwait(false)).ToList(); + if (addrs.Any(a => a == address)) + { + return address; + } + + addrs.Add(address); + var strings = addrs.Select(a => a.ToString()); + await _configApi.SetAsync("Swarm.AddrFilters", JToken.FromObject(strings), cancel).ConfigureAwait(false); + + _swarmService.WhiteList.Add(address); + + return address; + } + + public IEnumerable GetSwarmKnownPeers(CancellationToken cancel = default) + { + return _swarmService.KnownPeers; + } + + public async Task ConnectAsync(MultiAddress address, CancellationToken cancel = default) + { + Log.Debug($"Connecting to {address}"); + var conn = await _swarmService.ConnectAsync(address, cancel).ConfigureAwait(false); + Log.Debug($"Connected to {conn.RemotePeer.ConnectedAddress}"); + } + + public async Task ConnectAsync(Peer address, CancellationToken cancel = default) + { + Log.Debug($"Connecting to {address}"); + var conn = await _swarmService.ConnectAsync(address, cancel).ConfigureAwait(false); + Log.Debug($"Connected to {conn.RemotePeer.ConnectedAddress}"); + } + + public async Task DisconnectAsync(MultiAddress address, CancellationToken cancel = default) + { + await _swarmService.DisconnectAsync(address, cancel).ConfigureAwait(false); + } + + public async Task> ListAddressFiltersAsync(bool persist = false, + CancellationToken cancel = default) + { + try + { + var json = await _configApi.GetAsync("Swarm.AddrFilters", cancel).ConfigureAwait(false); + return json == null ? new MultiAddress[0] : json.Select(a => MultiAddress.TryCreate((string) a)).Where(a => a != null); + } + catch (KeyNotFoundException) + { + var strings = DefaultFilters.Select(a => a.ToString()); + await _configApi.SetAsync("Swarm.AddrFilters", JToken.FromObject(strings), cancel) + .ConfigureAwait(false); + + return DefaultFilters; + } + } + + public async Task> PeersAsync(CancellationToken cancel = default) + { + return _swarmService.KnownPeers.Where(p => p.ConnectedAddress != null); + } + + public async Task RemoveAddressFilterAsync(MultiAddress address, + bool persist = false, + CancellationToken cancel = default) + { + var addrs = (await ListAddressFiltersAsync(persist, cancel).ConfigureAwait(false)).ToList(); + if (addrs.All(a => a != address)) + { + return null; + } + + addrs.Remove(address); + var strings = addrs.Select(a => a.ToString()); + await _configApi.SetAsync("Swarm.AddrFilters", JToken.FromObject(strings), cancel).ConfigureAwait(false); + + _swarmService.WhiteList.Remove(address); + + return address; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/CoreApi/UnixFsApi.cs b/src/Catalyst.Core.Modules.Dfs/CoreApi/UnixFsApi.cs new file mode 100644 index 0000000000..5c9a0b4a6f --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/CoreApi/UnixFsApi.cs @@ -0,0 +1,408 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.Dag; +using Catalyst.Core.Lib.IO; +using Catalyst.Core.Modules.Dfs.UnixFs; +using ICSharpCode.SharpZipLib.Tar; +using Lib.P2P; +using ProtoBuf; + +namespace Catalyst.Core.Modules.Dfs.CoreApi +{ + internal sealed partial class UnixFsApi : IUnixFsApi + { + private readonly IDhtApi _dhtApi; + private readonly IBlockApi _blockApi; + private readonly IKeyApi _keyApi; + private readonly INameApi _nameApi; + private readonly DfsState _dfsState; + + /// + /// @TODO magic numbers be the devils work, WHY ONLY 174?? + /// + private const int DefaultLinksPerBlock = 174; + + public UnixFsApi(IDhtApi dhtApi, IBlockApi blockApi, IKeyApi keyApi, INameApi nameApi, DfsState dfsState) + { + _dhtApi = dhtApi; + _blockApi = blockApi; + _keyApi = keyApi; + _nameApi = nameApi; + _dfsState = dfsState; + } + + public async Task AddFileAsync(string path, + AddFileOptions options = default, + CancellationToken cancel = default) + { + await using var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read); + return await AddAsync(stream, Path.GetFileName(path), options, cancel).ConfigureAwait(false); + } + + public async Task AddTextAsync(string text, + AddFileOptions options = default, + CancellationToken cancel = default) + { + await using var ms = new MemoryStream(Encoding.UTF8.GetBytes(text), false); + return await AddAsync(ms, "", options, cancel).ConfigureAwait(false); + } + + public async Task AddAsync(Stream stream, + string name, + AddFileOptions options, + CancellationToken cancel) + { + options ??= new AddFileOptions(); + + // TODO: various options + if (options.Trickle) + { + throw new NotImplementedException("Trickle"); + } + + var blockService = GetBlockService(options); + + SizeChunker chunker = new(); + var nodes = await chunker.ChunkAsync(stream, name, options, blockService, _keyApi, cancel) + .ConfigureAwait(false); + + // Multiple nodes for the file? + var node = await BuildTreeAsync(nodes, options, cancel); + + // Wrap in directory? + if (options.Wrap) + { + var link = node.ToLink(name); + var wlinks = new[] + { + link + }; + node = await CreateDirectoryAsync(wlinks, options, cancel).ConfigureAwait(false); + } + else + { + node.Name = name; + } + + // Advertise the root node. + if (options.Pin && _dfsState.IsStarted) + { + await _dhtApi.ProvideAsync(node.Id, advertise: true, cancel: cancel).ConfigureAwait(false); + } + + // Return the file system node. + return node; + } + + private async Task BuildTreeAsync(IEnumerable nodes, + AddFileOptions options, + CancellationToken cancel) + { + while (true) + { + var fsNodes = nodes as UnixFsNode[] ?? nodes.ToArray(); + if (fsNodes.Length == 1) + { + return fsNodes.First(); + } + + // Bundle DefaultLinksPerBlock links into a block. + List tree = new(); + for (var i = 0;; ++i) + { + var bundle = fsNodes.Skip(DefaultLinksPerBlock * i).Take(DefaultLinksPerBlock); + var unixFsNodes = bundle.ToList(); + if (!unixFsNodes.Any()) + { + break; + } + + var node = await BuildTreeNodeAsync(unixFsNodes, options, cancel).ConfigureAwait(false); + tree.Add(node); + } + + nodes = tree; + } + } + + private async Task BuildTreeNodeAsync(IEnumerable nodes, + AddFileOptions options, + CancellationToken cancel) + { + var blockService = GetBlockService(options); + + // Build the DAG that contains all the file nodes. + var unixFsNodes = nodes as UnixFsNode[] ?? nodes.ToArray(); + var links = unixFsNodes.Select(n => n.ToLink()).ToArray(); + var fileSize = (ulong) unixFsNodes.Sum(n => n.Size); + var dagSize = unixFsNodes.Sum(n => n.DagSize); + var dm = new DataMessage + { + Type = DataType.File, + FileSize = fileSize, + BlockSizes = unixFsNodes.Select(n => (ulong) n.Size).ToArray() + }; + MemoryStream pb = new(); + Serializer.Serialize(pb, dm); + DagNode dag = new(pb.ToArray(), links); + + // Save it. + dag.Id = await blockService.PutAsync( + data: dag.ToArray(), + encoding: options.Encoding, + pin: options.Pin, + cancel: cancel).ConfigureAwait(false); + + return new UnixFsNode + { + Id = dag.Id, + Size = (long) dm.FileSize, + DagSize = dagSize + dag.Size, + Links = links + }; + } + + public async Task AddDirectoryAsync(string path, + bool recursive = true, + AddFileOptions options = default, + CancellationToken cancel = default) + { + options ??= new AddFileOptions(); + options.Wrap = false; + + // Add the files and sub-directories. + path = Path.GetFullPath(path); + var files = Directory.EnumerateFiles(path).OrderBy(s => s).Select(p => AddFileAsync(p, options, cancel)); + if (recursive) + { + var folders = Directory.EnumerateDirectories(path).OrderBy(s => s) + .Select(dir => AddDirectoryAsync(dir, true, options, cancel)); + files = files.Union(folders); + } + + var nodes = await Task.WhenAll(files).ConfigureAwait(false); + + // Create the DAG with links to the created files and sub-directories + var links = nodes.Select(node => node.ToLink()).ToArray(); + var fsn = await CreateDirectoryAsync(links, options, cancel).ConfigureAwait(false); + fsn.Name = Path.GetFileName(path); + return fsn; + } + + private async Task CreateDirectoryAsync(IEnumerable links, + AddFileOptions options, + CancellationToken cancel) + { + var dm = new DataMessage + { + Type = DataType.Directory + }; + MemoryStream pb = new(); + Serializer.Serialize(pb, dm); + var fileSystemLinks = links as IFileSystemLink[] ?? links.ToArray(); + DagNode dag = new(pb.ToArray(), fileSystemLinks); + + // Save it. + var cid = await GetBlockService(options).PutAsync( + data: dag.ToArray(), + multiHash: options.Hash, + encoding: options.Encoding, + pin: options.Pin, + cancel: cancel).ConfigureAwait(false); + + return new UnixFsNode + { + Id = cid, + Links = fileSystemLinks, + IsDirectory = true + }; + } + + public async Task ListFileAsync(string path, + CancellationToken cancel = default) + { + var r = await _nameApi.ResolveAsync(path, true, false, cancel).ConfigureAwait(false); + var cid = Cid.Decode(r.Remove(0, 6)); + + var block = await _blockApi.GetAsync(cid, cancel).ConfigureAwait(false); + + switch (cid.ContentType) + { + // TODO: A content-type registry should be used. + case "dag-pb": + // fall thru + break; + case "raw": + return new UnixFsNode + { + Id = cid, + Size = block.Size + }; + case "cms": + return new UnixFsNode + { + Id = cid, + Size = block.Size + }; + default: + throw new NotSupportedException($"Cannot read content type '{cid.ContentType}'."); + } + + DagNode dag = new(block.DataStream); + var dm = Serializer.Deserialize(dag.DataStream); + var fsn = new UnixFsNode + { + Id = cid, + Links = dag.Links.Select(l => new UnixFsLink + { + Id = l.Id, + Name = l.Name, + Size = l.Size + }).ToArray(), + IsDirectory = dm.Type == DataType.Directory, + Size = (long) (dm.FileSize ?? 0) + }; + + return fsn; + } + + public async Task ReadAllTextAsync(string path, CancellationToken cancel = default) + { + await using (var data = await ReadFileAsync(path, cancel).ConfigureAwait(false)) + { + using (StreamReader text = new(data)) + { + return await text.ReadToEndAsync().ConfigureAwait(false); + } + } + } + + public async Task ReadFileAsync(string path, CancellationToken cancel = default) + { + var r = await _nameApi.ResolveAsync(path, true, false, cancel).ConfigureAwait(false); + var cid = Cid.Decode(r.Remove(0, 6)); + return await UnixFs.UnixFs.CreateReadStreamAsync(cid, _blockApi, _keyApi, cancel).ConfigureAwait(false); + } + + public async Task ReadFileAsync(string path, + long offset, + long count = 0, + CancellationToken cancel = default) + { + var stream = await ReadFileAsync(path, cancel).ConfigureAwait(false); + return new SlicedStream(stream, offset, count); + } + + public async Task GetAsync(string path, + bool compress = false, + CancellationToken cancel = default) + { + var r = await _nameApi.ResolveAsync(path, true, false, cancel).ConfigureAwait(false); + var cid = Cid.Decode(r.Remove(0, 6)); + MemoryStream ms = new(); + await using (TarOutputStream tarStream = new(ms, 1)) + { + using (var archive = TarArchive.CreateOutputTarArchive(tarStream)) + { + archive.IsStreamOwner = false; + await AddTarNodeAsync(cid, cid.Encode(), tarStream, cancel).ConfigureAwait(false); + } + } + + ms.Position = 0; + return ms; + } + + private async Task AddTarNodeAsync(Cid cid, string name, TarOutputStream tar, CancellationToken cancel) + { + var block = await _blockApi.GetAsync(cid, cancel).ConfigureAwait(false); + var dm = new DataMessage + { + Type = DataType.Raw + }; + DagNode dag = null; + + if (cid.ContentType == "dag-pb") + { + dag = new DagNode(block.DataStream); + dm = Serializer.Deserialize(dag.DataStream); + } + + TarEntry entry = new(new TarHeader()); + var header = entry.TarHeader; + header.Mode = 0x1ff; // 777 in octal + header.LinkName = string.Empty; + header.UserName = string.Empty; + header.GroupName = string.Empty; + header.Version = "00"; + header.Name = name; + header.DevMajor = 0; + header.DevMinor = 0; + header.UserId = 0; + header.GroupId = 0; + header.ModTime = DateTime.Now; + + if (dm.Type == DataType.Directory) + { + header.TypeFlag = TarHeader.LF_DIR; + header.Size = 0; + tar.PutNextEntry(entry); + tar.CloseEntry(); + + // Recurse over files and subdirectories + foreach (var link in dag?.Links) + { + await AddTarNodeAsync(link.Id, $"{name}/{link.Name}", tar, cancel).ConfigureAwait(false); + } + } + else // Must be a file + { + var content = await ReadFileAsync(cid, cancel).ConfigureAwait(false); + header.TypeFlag = TarHeader.LF_NORMAL; + header.Size = content.Length; + tar.PutNextEntry(entry); + await content.CopyToAsync(tar, cancel); + tar.CloseEntry(); + } + } + + private IBlockApi GetBlockService(AddFileOptions options) + { + return options.OnlyHash + ? new HashOnlyBlockService() + : _blockApi; + } + } +} diff --git a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaDfsReader.cs b/src/Catalyst.Core.Modules.Dfs/DeltaDfsReader.cs similarity index 64% rename from src/Catalyst.Core.Modules.Consensus/Deltas/DeltaDfsReader.cs rename to src/Catalyst.Core.Modules.Dfs/DeltaDfsReader.cs index dd29dc90bf..63541fec8e 100644 --- a/src/Catalyst.Core.Modules.Consensus/Deltas/DeltaDfsReader.cs +++ b/src/Catalyst.Core.Modules.Dfs/DeltaDfsReader.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,20 +26,20 @@ using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Dfs; using Catalyst.Protocol.Deltas; -using LibP2P; +using Lib.P2P; using Serilog; -namespace Catalyst.Core.Modules.Consensus.Deltas +namespace Catalyst.Core.Modules.Dfs { /// - public class DeltaDfsReader : IDeltaDfsReader + public sealed class DeltaDfsReader : IDeltaDfsReader { - private readonly IDfs _dfs; + private readonly IDfsService _dfsService; private readonly ILogger _logger; - public DeltaDfsReader(IDfs dfs, ILogger logger) + public DeltaDfsReader(IDfsService dfsService, ILogger logger) { - _dfs = dfs; + _dfsService = dfsService; _logger = logger; } @@ -50,24 +50,21 @@ public bool TryReadDeltaFromDfs(Cid cid, { try { - using (var responseStream = _dfs.ReadAsync(cid, cancellationToken) + using var responseStream = _dfsService.UnixFsApi.ReadFileAsync(cid, cancellationToken) .ConfigureAwait(false) .GetAwaiter() - .GetResult() - ) + .GetResult(); + var uncheckedDelta = Delta.Parser.ParseFrom(responseStream); + var isValid = uncheckedDelta.IsValid(); + if (!isValid) { - var uncheckedDelta = Delta.Parser.ParseFrom(responseStream); - var isValid = uncheckedDelta.IsValid(); - if (!isValid) - { - _logger.Warning("Retrieved an invalid delta from the Dfs at address {hash}"); - delta = default; - return false; - } - - delta = uncheckedDelta; - return true; + _logger.Warning("Retrieved an invalid delta from the Dfs at address {hash}"); + delta = default; + return false; } + + delta = uncheckedDelta; + return true; } catch (Exception e) { diff --git a/src/Catalyst.Core.Modules.Dfs/DevDfs.cs b/src/Catalyst.Core.Modules.Dfs/DevDfs.cs deleted file mode 100644 index 70e5422d09..0000000000 --- a/src/Catalyst.Core.Modules.Dfs/DevDfs.cs +++ /dev/null @@ -1,122 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System.IO; -using System.Text; -using System.Threading; -using System.Threading.Tasks; -using Catalyst.Abstractions.Dfs; -using Catalyst.Abstractions.FileSystem; -using Catalyst.Abstractions.Hashing; -using Catalyst.Core.Lib.Config; -using Catalyst.Core.Lib.Util; -using Dawn; -using LibP2P; - -namespace Catalyst.Core.Modules.Dfs -{ - /// - /// A very naive implementation of the IDfs interface that simply relies on the file system. - /// This can only result in a 'Distributed' file system if the happens - /// to be shared network path. Otherwise, this can be used in integration tests, to ensure - /// the tests can be run locally and offline. - /// - /// - /// The hashing algorithm is also a simple one (>) to save time - /// in integration tests - /// - /// - public sealed class DevDfs : IDfs - { - private readonly DirectoryInfo _baseFolder; - private readonly IFileSystem _fileSystem; - private readonly IHashProvider _hashProvider; - - public DevDfs(IFileSystem fileSystem, - IHashProvider hashProvider, - string baseFolder = null) - { - Guard.Argument(hashProvider.HashingAlgorithm, nameof(hashProvider.HashingAlgorithm)) - .Require(h => h.DigestSize <= 159, h => - "The hashing algorithm needs to produce file names smaller than 255 base 32 characters or 160 bytes" + - $"but the default length for {hashProvider.HashingAlgorithm.GetType().Name} is {h.DigestSize}."); - - var dfsBaseFolder = baseFolder ?? Path.Combine(fileSystem.GetCatalystDataDir().FullName, - Constants.DfsDataSubDir); - - _baseFolder = new DirectoryInfo(dfsBaseFolder); - if (!_baseFolder.Exists) - { - _baseFolder.Create(); - } - - _fileSystem = fileSystem; - _hashProvider = hashProvider; - } - - /// - public async Task AddTextAsync(string utf8Content, CancellationToken cancellationToken = default) - { - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash(utf8Content)); - var filePath = Path.Combine(_baseFolder.FullName, cid); - - await _fileSystem.File.WriteAllTextAsync( - filePath, - utf8Content, Encoding.UTF8, cancellationToken); - - return cid; - } - - /// - public async Task ReadTextAsync(Cid cid, CancellationToken cancellationToken = default) - { - return await _fileSystem.File.ReadAllTextAsync(Path.Combine(_baseFolder.FullName, cid), - Encoding.UTF8, - cancellationToken); - } - - /// - public async Task AddAsync(Stream content, - string name = "", - CancellationToken cancellationToken = default) - { - var cid = CidHelper.CreateCid(_hashProvider.ComputeMultiHash(content)); - var filePath = Path.Combine(_baseFolder.FullName, cid); - - using (var file = _fileSystem.File.Create(filePath)) - { - content.Position = 0; - await content.CopyToAsync(file, cancellationToken).ConfigureAwait(false); - } - - return cid; - } - - /// - public async Task ReadAsync(Cid cid, CancellationToken cancellationToken = default) - { - return await Task.FromResult(_fileSystem.File.OpenRead(Path.Combine(_baseFolder.FullName, cid))) - .ConfigureAwait(false); - } - } -} diff --git a/src/Catalyst.Core.Modules.Dfs/Dfs.cs b/src/Catalyst.Core.Modules.Dfs/Dfs.cs deleted file mode 100644 index cb6ea85ccb..0000000000 --- a/src/Catalyst.Core.Modules.Dfs/Dfs.cs +++ /dev/null @@ -1,97 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System.IO; -using System.Threading; -using System.Threading.Tasks; -using Catalyst.Abstractions.Dfs; -using Catalyst.Abstractions.Hashing; -using LibP2P; -using Serilog; -using TheDotNetLeague.Ipfs.Core.Lib; -using TheDotNetLeague.Ipfs.Core.Lib.CoreApi; - -namespace Catalyst.Core.Modules.Dfs -{ - public sealed class Dfs : IDfs - { - private readonly ICoreApi _ipfs; - private readonly IHashProvider _hashProvider; - private readonly ILogger _logger; - - public Dfs(ICoreApi ipfsAdapter, IHashProvider hashProvider, ILogger logger) - { - _ipfs = ipfsAdapter; - _hashProvider = hashProvider; - _logger = logger; - } - - private AddFileOptions AddFileOptions() - { - return new AddFileOptions - { - Hash = _hashProvider.HashingAlgorithm.Name, - RawLeaves = true - }; - } - - /// - public async Task AddTextAsync(string content, CancellationToken cancellationToken = default) - { - var node = await _ipfs.FileSystem.AddTextAsync( - content, - AddFileOptions(), - cancellationToken); - _logger.Debug("Text added to Dfs with id {0}", node.Id); - return node.Id; - } - - /// - public async Task ReadTextAsync(Cid cid, - CancellationToken cancellationToken = default) - { - _logger.Debug("Reading content at path {0} from Dfs", cid); - return await _ipfs.FileSystem.ReadAllTextAsync(cid, cancellationToken).ConfigureAwait(false); - } - - /// - public async Task AddAsync(Stream content, - string name = "", - CancellationToken cancellationToken = default) - { - var node = await _ipfs.FileSystem - .AddAsync(content, name, AddFileOptions(), cancellationToken).ConfigureAwait(false); - _logger.Debug("Content {1} added to Dfs with id {0}", - node.Id, name + " "); - return node.Id; - } - - /// - public async Task ReadAsync(Cid cid, - CancellationToken cancellationToken = default) - { - _logger.Debug("Reading content at path {0} from Dfs", cid); - return await _ipfs.FileSystem.ReadFileAsync(cid, cancellationToken).ConfigureAwait(false); - } - } -} diff --git a/src/Catalyst.Core.Modules.Dfs/DfsGateway.cs b/src/Catalyst.Core.Modules.Dfs/DfsGateway.cs index 674f0dd200..415e2ca4ca 100644 --- a/src/Catalyst.Core.Modules.Dfs/DfsGateway.cs +++ b/src/Catalyst.Core.Modules.Dfs/DfsGateway.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Dfs/DfsModule.cs b/src/Catalyst.Core.Modules.Dfs/DfsModule.cs index 8a232561a7..9fc6942b5b 100644 --- a/src/Catalyst.Core.Modules.Dfs/DfsModule.cs +++ b/src/Catalyst.Core.Modules.Dfs/DfsModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,17 +22,113 @@ #endregion using Autofac; +using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Dfs; -using TheDotNetLeague.Ipfs.Core.Lib; +using Catalyst.Abstractions.Dfs.BlockExchange; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Dfs.Migration; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Options; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Core.Lib.Config; +using Catalyst.Core.Lib.Kernel; +using Catalyst.Core.Lib.P2P; +using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Core.Modules.Dfs.BlockExchange; +using Catalyst.Core.Modules.Dfs.CoreApi; +using Catalyst.Core.Modules.Dfs.Migration; +using Catalyst.Core.Modules.Keystore; +using Lib.P2P; +using Lib.P2P.Protocols; +using Lib.P2P.PubSub; +using Lib.P2P.Routing; +using Makaretu.Dns; +using SharpRepository.InMemoryRepository; +using SharpRepository.Repository; namespace Catalyst.Core.Modules.Dfs { - public class DfsModule : Module + public sealed class DfsModule : CatalystModule { - protected override void Load(ContainerBuilder builder) + protected override void LoadApi(ContainerBuilder builder) { - builder.RegisterType().As().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - } + builder.RegisterType().As().SingleInstance() + .OnActivated(e => e.Instance.PinApi = e.Context.Resolve()); + + builder.RegisterType().As().SingleInstance() + .OnActivated(e => e.Instance.BlockApi = e.Context.Resolve()); + + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + + builder.RegisterType().As().SingleInstance(); + } + + protected override void LoadService(ContainerBuilder builder) + { + builder.RegisterType() + .As() + .SingleInstance(); + + builder.RegisterType() + .As() + .SingleInstance().OnActivated(e => e.Instance.BlockService = e.Context.Resolve()); + + builder.RegisterType() + .As(); + + builder.RegisterType() + .As(); + + builder.RegisterType() + .As() + .SingleInstance(); + + builder.RegisterType() + .As() + .SingleInstance(); + + builder.RegisterType() + .As() + .SingleInstance(); + + builder.RegisterType() + .As(); + + builder.RegisterType() + .As(); + + builder.RegisterType>().As>().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + + builder.RegisterType() + .As(); + } + + protected override void LoadOptions(ContainerBuilder builder) + { + builder.RegisterType().SingleInstance(); + builder.RegisterType().SingleInstance(); + builder.RegisterType().SingleInstance() + .WithParameter("dfsDirectory", Constants.DfsDataSubDir); + //Disable Mdns in dfs as it causes a memoryleak/outofmemory exception + builder.RegisterType().SingleInstance().WithProperty("DisableMdns", true).WithProperty("UsePeerRepository", true); + builder.RegisterType().SingleInstance().WithProperty("DefaultKeyType", "ed25519"); + builder.RegisterType().SingleInstance(); + } } } diff --git a/src/Catalyst.Core.Modules.Dfs/DfsService.cs b/src/Catalyst.Core.Modules.Dfs/DfsService.cs new file mode 100644 index 0000000000..82b5cad1e5 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/DfsService.cs @@ -0,0 +1,563 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.BlockExchange; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Dfs.Migration; +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Options; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Core.Lib.P2P; +using Catalyst.Core.Modules.Dfs.BlockExchange; +using Common.Logging; +using Common.Logging.Serilog; +using Lib.P2P; +using Lib.P2P.Cryptography; +using Lib.P2P.Discovery; +using Lib.P2P.Protocols; +using Lib.P2P.PubSub; +using Lib.P2P.Routing; +using Lib.P2P.SecureCommunication; +using Makaretu.Dns; +using MultiFormats; +using Serilog; + +namespace Catalyst.Core.Modules.Dfs +{ + public class DfsService : IDfsService + { + static DfsService() { LogManager.Adapter = new SerilogFactoryAdapter(Log.Logger); } + + /// + /// Determines if the engine has started. + /// + /// + /// true if the engine has started; otherwise, false. + /// + /// + /// + public bool IsStarted => _stopTasks.Count > 0; + + private readonly DfsState _dfsState; + private readonly IHashProvider _hashProvider; + private ConcurrentBag> _stopTasks = new(); + private readonly IPeerRepository _peerRepository; + + public DfsService( + Peer localPeer, + IBitswapService bitSwapService, + IDhtService dhtService, + Ping1 pingService, + IPubSubService pubSubService, + ISwarmService swarmService, + IBootstrapApi bootstrapApi, + IConfigApi configApi, + IBitSwapApi bitSwapApi, + IBlockApi blockApi, + IBlockRepositoryApi blockRepositoryApi, + IDagApi dagApi, + IDhtApi dhtApi, + IDnsApi dnsApi, + IUnixFsApi unixFsApi, + IKeyApi keyApi, + INameApi nameApi, + IObjectApi objectApi, + IPinApi pinApi, + IPubSubApi pubSubApi, + IStatsApi statsApi, + ISwarmApi swarmApi, + IHashProvider hashProvider, + DfsOptions dfsOptions, + DfsState dfsState, + IEnumerable pluginServices, + IMigrationManager migrationManager, + IPeerRepository peerRepository + ) + { + LocalPeer = localPeer; + + BitSwapService = bitSwapService; + DhtService = dhtService; + PingService = pingService; + PubSubService = pubSubService; + SwarmService = swarmService; + + BootstrapApi = bootstrapApi; + ConfigApi = configApi; + BitSwapApi = bitSwapApi; + BlockApi = blockApi; + BlockRepositoryApi = blockRepositoryApi; + DagApi = dagApi; + DhtApi = dhtApi; + DnsApi = dnsApi; + UnixFsApi = unixFsApi; + KeyApi = keyApi; + NameApi = nameApi; + ObjectApi = objectApi; + PinApi = pinApi; + PubSubApi = pubSubApi; + StatsApi = statsApi; + SwarmApi = swarmApi; + Options = dfsOptions; + _hashProvider = hashProvider; + _dfsState = dfsState; + MigrationManager = migrationManager; + PluginServices = pluginServices; + + _peerRepository = peerRepository; + + InitAsync().Wait(); + } + + internal virtual AddFileOptions AddFileOptions() + { + return new AddFileOptions + { + Hash = _hashProvider.HashingAlgorithm.Name, + RawLeaves = true + }; + } + + public IEnumerable PluginServices { set; get; } + + /// + /// The configuration options. + /// + public DfsOptions Options { get; set; } + + private async Task InitAsync() + { + //Swarm + Log.Debug("Building swarm service"); + + SwarmService.LocalPeer = LocalPeer; + SwarmService.LocalPeerKey = + Key.CreatePrivateKey(await KeyApi.GetPrivateKeyAsync("self").ConfigureAwait(false)); + SwarmService.NetworkProtector = Options.Swarm.PrivateNetworkKey == null + ? null + : new Psk1Protector + { + Key = Options.Swarm.PrivateNetworkKey + }; + + if (Options.Swarm.PrivateNetworkKey != null) + { + Log.Debug($"Private network {Options.Swarm.PrivateNetworkKey.Fingerprint().ToHexString()}"); + } + + Log.Debug("Building PubSub service"); + + Log.Debug("Built PubSub service"); + } + + /// + /// Starts the network services. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// Starts the various IPFS and Lib.P2P network services. This should + /// be called after any configuration changes. + /// + /// + /// When the engine is already started. + /// + public async Task StartAsync() + { + _dfsState.IsStarted = true; + if (_stopTasks.Count > 0) + { + throw new Exception("IPFS engine is already started."); + } + + // Repository must be at the correct version. + await MigrationManager.MirgrateToVersionAsync(MigrationManager.LatestVersion).ConfigureAwait(false); + + Log.Debug("starting " + LocalPeer.Id); + + // Everybody needs the swarm. + _stopTasks.Add(async () => { await SwarmService.StopAsync().ConfigureAwait(false); }); + await SwarmService.StartAsync().ConfigureAwait(false); + + var peerManager = new PeerManager + { + SwarmService = SwarmService + }; + await peerManager.StartAsync().ConfigureAwait(false); + _stopTasks.Add(async () => { await peerManager.StopAsync().ConfigureAwait(false); }); + + // Start the primary services. + var tasks = new List> + { + async () => + { + _stopTasks.Add(async () => await BitSwapService.StopAsync().ConfigureAwait(false)); + await BitSwapService.StartAsync().ConfigureAwait(false); + }, + async () => + { + _stopTasks.Add(async () => await DhtService.StopAsync().ConfigureAwait(false)); + await DhtService.StartAsync().ConfigureAwait(false); + }, + async () => + { + _stopTasks.Add(async () => await PingService.StopAsync().ConfigureAwait(false)); + await PingService.StartAsync().ConfigureAwait(false); + }, + async () => + { + _stopTasks.Add(async () => await PubSubService.StopAsync().ConfigureAwait(false)); + await PubSubService.StartAsync().ConfigureAwait(false); + } + }; + + foreach (var service in PluginServices) + { + tasks.Add(async () => + { + _stopTasks.Add(async () => await service.StopAsync().ConfigureAwait(false)); + await service.StartAsync().ConfigureAwait(false); + }); + } + + Log.Debug("waiting for services to start"); + await Task.WhenAll(tasks.Select(t => t())).ConfigureAwait(false); + + // Starting listening to the swarm. + var json = await ConfigApi.GetAsync("Addresses.Swarm").ConfigureAwait(false); + var numberListeners = 0; + foreach (string a in json) + { + try + { + await SwarmService.StartListeningAsync(a).ConfigureAwait(false); + ++numberListeners; + } + catch (Exception e) + { + Log.Warning($"Listener failure for '{a}'", e); + } + } + + if (numberListeners == 0) + { + Log.Error("No listeners were created."); + } + + // Now that the listener addresses are established, the discovery + // services can begin. + MulticastService multicast = null; + if (!Options.Discovery.DisableMdns) + { + multicast = new MulticastService(); +#pragma warning disable CS1998 + _stopTasks.Add(async () => multicast.Dispose()); +#pragma warning restore CS1998 + } + + var autodialer = new AutoDialer(SwarmService) + { + MinConnections = Options.Swarm.MinConnections + }; +#pragma warning disable CS1998 + _stopTasks.Add(async () => autodialer.Dispose()); +#pragma warning restore CS1998 + + tasks = new List> + { + // Bootstrap discovery + async () => + { + var bootstrap = new Bootstrap + { + Addresses = Options.Discovery.UsePeerRepository ? _peerRepository.GetAll().Select(x=>x.Address) : await BootstrapApi.ListAsync() + }; + bootstrap.PeerDiscovered += OnPeerDiscovered; + _stopTasks.Add(async () => await bootstrap.StopAsync().ConfigureAwait(false)); + await bootstrap.StartAsync().ConfigureAwait(false); + }, + + async () => + { + if (Options.Discovery.DisableRandomWalk) + return; + var randomWalk = new RandomWalk {Dht = DhtApi}; + _stopTasks.Add(async () => await randomWalk.StopAsync().ConfigureAwait(false)); + await randomWalk.StartAsync().ConfigureAwait(false); + } + }; + Log.Debug("waiting for discovery services to start"); + await Task.WhenAll(tasks.Select(t => t())).ConfigureAwait(false); + + multicast?.Start(); + + Log.Debug("started"); + } + + /// + /// Stops the running services. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// Multiple calls are okay. + /// + public async Task StopAsync() + { + Log.Debug("stopping"); + try + { + var tasks = _stopTasks.ToArray(); + _stopTasks = new ConcurrentBag>(); + await Task.WhenAll(tasks.Select(t => t())).ConfigureAwait(false); + } + catch (Exception e) + { + Log.Error("Failure when stopping the engine", e); + } + + // Many services use cancellation to stop. A cancellation may not run + // immediately, so we need to give them some. + // TODO: Would be nice to make this deterministic. + await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false); + + Log.Debug("stopped"); + } + + /// + /// A synchronous start. + /// + /// + /// Calls and waits for it to complete. + /// + public void Start() { StartAsync().ConfigureAwait(false).GetAwaiter().GetResult(); } + + /// + /// A synchronous stop. + /// + /// + /// Calls and waits for it to complete. + /// + public void Stop() + { + Log.Debug("stopping"); + try + { + var tasks = _stopTasks.ToArray(); + _stopTasks = new ConcurrentBag>(); + foreach (var task in tasks) + { + task().ConfigureAwait(false).GetAwaiter().GetResult(); + } + } + catch (Exception e) + { + Log.Error("Failure when stopping the engine", e); + } + } + +#pragma warning disable VSTHRD100 // Avoid async void methods + /// + /// Fired when a peer is discovered. + /// + /// + /// + /// + /// Registers the peer with the . + /// + private void OnPeerDiscovered(object sender, Peer peer) +#pragma warning restore VSTHRD100 // Avoid async void methods + { + try + { + SwarmService.RegisterPeer(peer); + + if (Options.Discovery.UsePeerRepository) + { + var address = peer.Addresses.FirstOrDefault(); + if (address != null) + { + var newPeer = new Lib.P2P.Models.Peer + { + Address = address + }; + if (!_peerRepository.Exists(newPeer.DocumentId)) + { + _peerRepository.Add(newPeer); + } + } + } + } + catch (Exception ex) + { + Log.Warning("failed to register peer " + peer, ex); + + // eat it, nothing we can do. + } + } + + #region Class members + + /// + /// Determines latency to a peer. + /// + public Ping1 PingService { get; } + + /// + /// Provides access to the local peer. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a . + /// + public Peer LocalPeer { get; } + + /// + /// Manages the version of the repository. + /// + public IMigrationManager MigrationManager { get; set; } + + #region Dfs Services + + /// + /// Manages communication with other peers. + /// + public ISwarmService SwarmService { get; } + + /// + /// Manages publishng and subscribing to messages. + /// + public IPubSubService PubSubService { get; } + + /// + /// Exchange blocks with other peers. + /// + public IBitswapService BitSwapService { get; } + + /// + /// Finds information with a distributed hash table. + /// + public IDhtService DhtService { get; } + + #endregion + + #region CoreAPI Support + + /// + public IBitSwapApi BitSwapApi { get; set; } + + /// + public IBlockApi BlockApi { get; set; } + + /// + public IBlockRepositoryApi BlockRepositoryApi { get; set; } + + /// + public IBootstrapApi BootstrapApi { get; set; } + + /// + public IConfigApi ConfigApi { get; set; } + + /// + public IDagApi DagApi { get; set; } + + /// + public IDhtApi DhtApi { get; set; } + + /// + public IDnsApi DnsApi { get; set; } + + /// + public IUnixFsApi UnixFsApi { get; set; } + + /// + public IKeyApi KeyApi { get; set; } + + /// + public INameApi NameApi { get; set; } + + /// + public IObjectApi ObjectApi { get; set; } + + /// + public IPinApi PinApi { get; set; } + + /// + public IPubSubApi PubSubApi { get; set; } + + /// + public ISwarmApi SwarmApi { get; set; } + + /// + public IStatsApi StatsApi { get; set; } + + #endregion + + #endregion + + #region IDisposable Support + + private bool _disposedValue; // To detect redundant calls + + /// + /// Releases the unmanaged and optionally managed resources. + /// + /// + /// true to release both managed and unmanaged resources; false + /// to release only unmanaged resources. + /// + protected virtual void Dispose(bool disposing) + { + if (_disposedValue) + { + return; + } + + _disposedValue = true; + + if (!disposing) + { + return; + } + + Stop(); + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() { Dispose(true); } + + #endregion + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/DfsState.cs b/src/Catalyst.Core.Modules.Dfs/DfsState.cs new file mode 100644 index 0000000000..fdb46ab90e --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/DfsState.cs @@ -0,0 +1,30 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs +{ + public class DfsState + { + public bool IsStarted { set; get; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/Extensions/CidExtensions.cs b/src/Catalyst.Core.Modules.Dfs/Extensions/CidExtensions.cs new file mode 100644 index 0000000000..041e178696 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/Extensions/CidExtensions.cs @@ -0,0 +1,62 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Core.Lib.Extensions; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs.Extensions +{ + public static class CidExtensions + { + // @TODO get this from hashing algorithm. as a param to the extension method + private const string Encoding = "base32"; + + /// + /// + /// + /// + /// + public static Cid ToCid(this MultiHash multiHash) + { + return new Cid + { + Version = 1, + Hash = multiHash, + ContentType = "dag-pb", + Encoding = Encoding + }; + } + + public static Cid ToCid(this byte[] cid) + { + return Cid.Decode(MultiBase.Encode(cid, Encoding)); + } + + public static Cid ToCid(this string cid) + { + //MultiBase.Encode(cid.ToUtf8Bytes(), Encoding) + return Cid.Decode(cid); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/IpfsAdapter.cs b/src/Catalyst.Core.Modules.Dfs/IpfsAdapter.cs deleted file mode 100644 index dde55b142d..0000000000 --- a/src/Catalyst.Core.Modules.Dfs/IpfsAdapter.cs +++ /dev/null @@ -1,186 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System.Collections.Generic; -using System.IO; -using System.Linq; -using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.Dfs; -using Catalyst.Abstractions.FileSystem; -using Catalyst.Abstractions.Types; -using Catalyst.Core.Lib.Config; -using Common.Logging; -using Common.Logging.Serilog; -using LibP2P; -using LibP2P.Cryptography; -using Serilog; -using TheDotNetLeague.Ipfs.Abstractions; -using TheDotNetLeague.Ipfs.Core.Lib; -using TheDotNetLeague.MultiFormats.MultiAddress; -using TheDotNetLeague.MultiFormats.MultiBase; - -namespace Catalyst.Core.Modules.Dfs -{ - /// - /// Modifies the IPFS behaviour to meet the Catalyst requirements. - /// - /// - /// The IPFS engine is lazy, it is only started when needed. - /// - public sealed class IpfsAdapter : IIpfsAdapter - { - /// - /// An IPFS implementation, commonly called an IPFS node/ - /// - private IpfsEngine _ipfs; - - private bool _isStarted; - private readonly object _startingLock = new object(); - private readonly ILogger _logger; - - static IpfsAdapter() - { - LogManager.Adapter = new SerilogFactoryAdapter(Log.Logger); - } - - public IpfsAdapter(IPasswordManager passwordReader, - IFileSystem fileSystem, - ILogger logger, - string swarmKey = "07a8e9d0c43400927ab274b7fa443596b71e609bacae47bd958e5cd9f59d6ca3", - IEnumerable seedServers = null) - { - if (seedServers == null || seedServers.Count() == 0) - { - seedServers = new[] - { - new MultiAddress("/ip4/46.101.132.61/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZdtAkDHgs8MDwwhtyLu8JpYitY4Nk8jmyGgQ4Gt3VKNson"), - new MultiAddress("/ip4/188.166.13.135/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZe2AAPTCoujCxhJHECaySDEsPrEz9W2u7uo6hAbJhYzhPg"), - new MultiAddress("/ip4/167.172.73.132/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZe1E9wXdykR6h3Q9EaQcQc6hdNAXyCTEzoGfcA2wQgCRyg") - }; - } - - _logger = logger; - - // The password is used to access the private keys. - var password = passwordReader.RetrieveOrPromptAndAddPasswordToRegistry(PasswordRegistryTypes.IpfsPassword, "Please provide your IPFS password"); - _ipfs = new IpfsEngine(password); - _ipfs.Options.KeyChain.DefaultKeyType = Constants.KeyChainDefaultKeyType; - - // The IPFS repository is inside the catalyst home folder. - _ipfs.Options.Repository.Folder = Path.Combine( - fileSystem.GetCatalystDataDir().FullName, - Constants.DfsDataSubDir); - - // The seed nodes for the catalyst network. - _ipfs.Options.Discovery.BootstrapPeers = seedServers; - - // Do not use the public IPFS network, use a private network - // of catalyst only nodes. - _ipfs.Options.Swarm.PrivateNetworkKey = new PreSharedKey - { - Value = swarmKey.ToHexBuffer() - }; - - _logger.Information("IPFS configured."); - } - - /// - /// Starts the engine if required. - /// - /// - /// The started IPFS Engine. - /// - private IpfsEngine Start() - { - if (_isStarted) - { - return _ipfs; - } - - lock (_startingLock) - { - if (_isStarted) - { - return _ipfs; - } - - _ipfs.Start(); - _isStarted = true; - _logger.Information("IPFS started."); - } - - return _ipfs; - } - - public IBitswapApi Bitswap => Start().Bitswap; - - public IBlockApi Block => Start().Block; - - public IBlockRepositoryApi BlockRepository => Start().BlockRepository; - - public IBootstrapApi Bootstrap => Start().Bootstrap; - - public IConfigApi Config => _ipfs.Config; - public IpfsEngineOptions Options => _ipfs.Options; - - public IDagApi Dag => Start().Dag; - - public IDhtApi Dht => Start().Dht; - - public IDnsApi Dns => Start().Dns; - - public IFileSystemApi FileSystem => Start().FileSystem; - - public IGenericApi Generic => Start().Generic; - - public IKeyApi Key => _ipfs.Key; - - public INameApi Name => Start().Name; - - public IObjectApi Object => Start().Object; - - public IPinApi Pin => Start().Pin; - - public IPubSubApi PubSub => Start().PubSub; - - public IStatsApi Stats => Start().Stats; - - public ISwarmApi Swarm => Start().Swarm; - - private void Dispose(bool disposing) - { - if (!disposing) - { - return; - } - - _ipfs?.Dispose(); - _ipfs = null; - } - - public void Dispose() - { - Dispose(true); - } - } -} diff --git a/src/Catalyst.Core.Modules.Dfs/LinkedData/CborFormat.cs b/src/Catalyst.Core.Modules.Dfs/LinkedData/CborFormat.cs new file mode 100644 index 0000000000..d737f1643d --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/LinkedData/CborFormat.cs @@ -0,0 +1,40 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using PeterO.Cbor; + +namespace Catalyst.Core.Modules.Dfs.LinkedData +{ + /// + /// Linked data as a CBOR message. + /// + /// RFC 7049 + public class CborFormat : ILinkedDataFormat + { + /// + public CBORObject Deserialise(byte[] data) { return CBORObject.DecodeFromBytes(data); } + + /// + public byte[] Serialize(CBORObject data) { return data.EncodeToBytes(); } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/LinkedData/ILinkedDataFormat.cs b/src/Catalyst.Core.Modules.Dfs/LinkedData/ILinkedDataFormat.cs new file mode 100644 index 0000000000..2e5fd38df3 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/LinkedData/ILinkedDataFormat.cs @@ -0,0 +1,63 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using PeterO.Cbor; + +namespace Catalyst.Core.Modules.Dfs.LinkedData +{ + /// + /// A specific format for linked data. + /// + /// + /// Allows the conversion between the canonincal form of linked data and its binary + /// representation in a specific format. + /// + /// The canonical form is a . + /// + /// + public interface ILinkedDataFormat + { + /// + /// Convert the binary represention into the equivalent canonical form. + /// + /// + /// The linked data encoded in a specific format. + /// + /// + /// The canonical representation of the . + /// + CBORObject Deserialise(byte[] data); + + /// + /// Convert the canonical data into the specific format. + /// + /// + /// The canonical data to convert. + /// + /// + /// The binary representation of the encoded + /// in the specific format. + /// + byte[] Serialize(CBORObject data); + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/LinkedData/IpldRegistry.cs b/src/Catalyst.Core.Modules.Dfs/LinkedData/IpldRegistry.cs new file mode 100644 index 0000000000..5bdc8e09fb --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/LinkedData/IpldRegistry.cs @@ -0,0 +1,69 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.LinkedData +{ + /// + /// Metadata on . + /// + public static class IpldRegistry + { + /// + /// All the supported IPLD formats. + /// + /// + /// The key is the multicodec name. + /// The value is an object that implements . + /// + public static Dictionary Formats; + + static IpldRegistry() + { + Formats = new Dictionary(); + Register("dag-cbor"); + Register("dag-pb"); + Register("raw"); + } + + /// + /// Register a new IPLD format. + /// + /// + /// A Type that implements . + /// + /// + /// The multicodec name. + /// + public static void Register(string name) where T : ILinkedDataFormat, new() { Formats.Add(name, new T()); } + + /// + /// Remove the IPLD format. + /// + /// + /// The multicodec name. + /// + public static void Deregister(string name) { Formats.Remove(name); } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/LinkedData/ProtobufFormat.cs b/src/Catalyst.Core.Modules.Dfs/LinkedData/ProtobufFormat.cs new file mode 100644 index 0000000000..b4f63bc263 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/LinkedData/ProtobufFormat.cs @@ -0,0 +1,84 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Linq; +using Catalyst.Core.Lib.Dag; +using Catalyst.Core.Modules.Hashing; +using Lib.P2P; +using MultiFormats.Registry; +using PeterO.Cbor; + +namespace Catalyst.Core.Modules.Dfs.LinkedData +{ + /// + /// Linked data as a protobuf message. + /// + /// + /// This is the original legacy format used by the IPFS . + /// + public sealed class ProtobufFormat : ILinkedDataFormat + { + public ProtobufFormat() + { + new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + } + + /// + public CBORObject Deserialise(byte[] data) + { + using (MemoryStream ms = new(data, false)) + { + DagNode node = new(ms); + var links = node.Links + .Select(link => CBORObject.NewMap() + .Add("Cid", CBORObject.NewMap() + .Add("/", link.Id.Encode()) + ) + .Add("Name", link.Name) + .Add("Size", link.Size)) + .ToArray(); + var cbor = CBORObject.NewMap() + .Add("data", node.DataBytes) + .Add("links", links); + return cbor; + } + } + + /// + public byte[] Serialize(CBORObject data) + { + var links = data["links"].Values + .Select(link => new DagLink( + link["Name"].AsString(), + Cid.Decode(link["Cid"]["/"].AsString()), + link["Size"].AsInt64())); + DagNode node = new(data["data"].GetByteString(), links); + using (MemoryStream ms = new()) + { + node.Write(ms); + return ms.ToArray(); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/LinkedData/RawFormat.cs b/src/Catalyst.Core.Modules.Dfs/LinkedData/RawFormat.cs new file mode 100644 index 0000000000..3785a13187 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/LinkedData/RawFormat.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using PeterO.Cbor; + +namespace Catalyst.Core.Modules.Dfs.LinkedData +{ + /// + /// Unlinked data. + /// + public class RawFormat : ILinkedDataFormat + { + /// + public CBORObject Deserialise(byte[] data) + { + return CBORObject.NewMap().Add("data", data) + .Add("links", null); + } + + /// + public byte[] Serialize(CBORObject data) { return data["data"].GetByteString(); } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/Migration/MigrateTo1.cs b/src/Catalyst.Core.Modules.Dfs/Migration/MigrateTo1.cs new file mode 100644 index 0000000000..c0ecffd4b9 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/Migration/MigrateTo1.cs @@ -0,0 +1,117 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.Migration; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.FileSystem; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs.Migration +{ + internal class MigrateTo1 : IMigration + { + private sealed class Pin1 + { + public Cid Id; + } + + public int Version => 1; + + public bool CanUpgrade => true; + + public bool CanDowngrade => true; + + public async Task DowngradeAsync(RepositoryOptions options, CancellationToken cancel = default) + { + var path = Path.Combine(options.Folder, "pins"); + DirectoryInfo folder = new(path); + if (!folder.Exists) + { + return; + } + + var store = new FileStore + { + Folder = path, + NameToKey = (cid) => cid.Hash.ToBase32(), + KeyToName = (key) => new MultiHash(Base32.FromBase32(key)) + }; + + var files = folder.EnumerateFiles().Where(fi => fi.Length != 0); + foreach (var fi in files) + { + try + { + var name = store.KeyToName(fi.Name); + var pin = await store.GetAsync(name, cancel).ConfigureAwait(false); + File.Create(Path.Combine(store.Folder, pin.Id)); + File.Delete(store.GetPath(name)); + } + catch + { + // ignored + } + } + } + + public async Task UpgradeAsync(RepositoryOptions options, CancellationToken cancel = default) + { + var path = Path.Combine(options.Folder, "pins"); + DirectoryInfo folder = new(path); + if (!folder.Exists) + { + return; + } + + var store = new FileStore + { + Folder = path, + NameToKey = (cid) => cid.Hash.ToBase32(), + KeyToName = (key) => new MultiHash(key.FromBase32()) + }; + + var files = folder.EnumerateFiles().Where(fi => fi.Length == 0); + foreach (var fi in files) + { + try + { + var cid = Cid.Decode(fi.Name); + await store.PutAsync(cid, new Pin1 + { + Id = cid + }, cancel).ConfigureAwait(false); + File.Delete(fi.FullName); + } + catch + { + // ignored + } + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/Migration/MigrationManager.cs b/src/Catalyst.Core.Modules.Dfs/Migration/MigrationManager.cs new file mode 100644 index 0000000000..13d843292d --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/Migration/MigrationManager.cs @@ -0,0 +1,147 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.Migration; +using Catalyst.Abstractions.Options; +using Common.Logging; + +namespace Catalyst.Core.Modules.Dfs.Migration +{ + /// + /// Allows migration of the repository. + /// + public sealed class MigrationManager : IMigrationManager + { + private readonly RepositoryOptions _options; + private static readonly ILog Log = LogManager.GetLogger(typeof(MigrationManager)); + + /// + /// Creates a new instance of the class + /// for the specifed . + /// + public MigrationManager(RepositoryOptions options) + { + _options = options; + Migrations = typeof(MigrationManager).Assembly.GetTypes() + .Where(x => typeof(IMigration).IsAssignableFrom(x) && !x.IsInterface && !x.IsAbstract) + .Select(x => (IMigration) Activator.CreateInstance(x)) + .OrderBy(x => x.Version) + .ToList(); + } + + /// + /// The list of migrations that can be performed. + /// + public List Migrations { get; } + + /// + /// Gets the latest supported version number of a repository. + /// + public int LatestVersion => Migrations.Last().Version; + + /// + /// Gets the current vesion number of the repository. + /// + public int CurrentVersion + { + get + { + var path = VersionPath(); + if (!File.Exists(path)) + { + return 0; + } + + using (StreamReader reader = new(path)) + { + var s = reader.ReadLine(); + return int.Parse(s ?? throw new NullReferenceException(("stream null"))); + } + } + private set => File.WriteAllText(VersionPath(), value.ToString(CultureInfo.InvariantCulture)); + } + + /// + /// Upgrade/downgrade to the specified version. + /// + /// + /// The required version of the repository. + /// + /// + /// + /// + public async Task MirgrateToVersionAsync(int version, CancellationToken cancel = default) + { + if (version != 0 && Migrations.All(m => m.Version != version)) + { + throw new ArgumentOutOfRangeException(nameof(version), $@"Repository version '{version.ToString()}' is unknown."); + } + + var currentVersion = CurrentVersion; + var increment = CurrentVersion < version ? 1 : -1; + while (currentVersion != version) + { + var nextVersion = currentVersion + increment; + Log.InfoFormat("Migrating to version {0}", nextVersion.ToString()); + + if (increment > 0) + { + var migration = Migrations.FirstOrDefault(m => m.Version == nextVersion); + if (migration != null && migration.CanUpgrade) + { + await migration.UpgradeAsync(_options, cancel); + } + } + else if (increment < 0) + { + var migration = Migrations.FirstOrDefault(m => m.Version == currentVersion); + if (migration != null && migration.CanDowngrade) + { + await migration.DowngradeAsync(_options, cancel); + } + } + + CurrentVersion = nextVersion; + currentVersion = nextVersion; + } + } + + /// + /// Gets the FQN of the version file. + /// + /// + /// The path to the version file. + /// + private string VersionPath() + { + return Path.Combine(_options.ExistingFolder(), "version"); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/RandomWalk.cs b/src/Catalyst.Core.Modules.Dfs/RandomWalk.cs new file mode 100644 index 0000000000..f3443e7982 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/RandomWalk.cs @@ -0,0 +1,147 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Common.Logging; +using Lib.P2P; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs +{ + /// + /// Periodically queries the DHT to discover new peers. + /// + /// + /// A backgroud task is created to query the DHT. It is designed + /// to run often at startup and then less often at time increases. + /// + public sealed class RandomWalk : IService + { + private static ILog log = LogManager.GetLogger(typeof(RandomWalk)); + private CancellationTokenSource _cancel; + + /// + /// The Distributed Hash Table to query. + /// + internal IDhtApi Dht { get; set; } + + /// + /// The time to wait until running the query. + /// + public TimeSpan Delay = TimeSpan.FromSeconds(5); + + /// + /// The time to add to the . + /// + public TimeSpan DelayIncrement = TimeSpan.FromSeconds(10); + + /// + /// The maximum . + /// + public TimeSpan DelayMax = TimeSpan.FromMinutes(9); + + /// + /// Start a background process that will run a random + /// walk every . + /// + public Task StartAsync() + { + if (_cancel != null) + { + throw new Exception("Already started."); + } + + _cancel = new CancellationTokenSource(); + _ = RunnerAsync(_cancel.Token); + + log.Debug("started"); + return Task.CompletedTask; + } + + /// + /// Stop the background process. + /// + public Task StopAsync() + { + _cancel?.Cancel(); + _cancel?.Dispose(); + _cancel = null; + + log.Debug("stopped"); + return Task.CompletedTask; + } + + /// + /// The background process. + /// + private async Task RunnerAsync(CancellationToken cancellation) + { + while (!cancellation.IsCancellationRequested) + { + try + { + await Task.Delay(Delay, cancellation); + await RunQueryAsync(cancellation).ConfigureAwait(false); + log.Debug("query finished"); + Delay += DelayIncrement; + if (Delay > DelayMax) + { + Delay = DelayMax; + } + } + catch (TaskCanceledException) + { + // eat it. + } + catch (Exception e) + { + log.Error("run query failed", e); + + // eat all exceptions + } + } + } + + private async Task RunQueryAsync(CancellationToken cancel = default) + { + // Tests may not set a DHT. + if (Dht == null) + { + return; + } + + log.Debug("Running a query"); + + // Get a random peer id. + var x = new byte[32]; + Random rng = new(); + rng.NextBytes(x); + var id = MultiHash.ComputeHash(x); + + await Dht.FindPeerAsync(id, cancel).ConfigureAwait(false); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/UnixFs/ChunkedStream.cs b/src/Catalyst.Core.Modules.Dfs/UnixFs/ChunkedStream.cs new file mode 100644 index 0000000000..547c3ca33c --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/UnixFs/ChunkedStream.cs @@ -0,0 +1,182 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Keystore; +using Catalyst.Core.Lib.Dag; +using Lib.P2P; +using ProtoBuf; + +namespace Catalyst.Core.Modules.Dfs.UnixFs +{ + /// + /// Provides read-only access to a chunked file. + /// + /// + /// Internal class to support . + /// + public sealed class ChunkedStream : Stream + { + private sealed class BlockInfo + { + public Cid Id; + public long Position; + } + + private readonly List blocks = new(); + + /// + /// Creates a new instance of the class with + /// the specified and . + /// + /// + /// + /// + internal ChunkedStream(IBlockApi blockService, IKeyApi keyChain, IDagNode dag) + { + BlockService = blockService; + KeyChain = keyChain; + var links = dag.Links.ToArray(); + var dm = Serializer.Deserialize(dag.DataStream); + if (dm.FileSize != null) + { + Length = (long) dm.FileSize; + } + + ulong position = 0; + for (var i = 0; i < dm.BlockSizes.Length; ++i) + { + blocks.Add(new BlockInfo + { + Id = links[i].Id, + Position = (long) position + }); + position += dm.BlockSizes[i]; + } + } + + private IBlockApi BlockService { get; } + private IKeyApi KeyChain { get; } + + /// + public override long Length { get; } + + /// + public override void SetLength(long value) { throw new NotSupportedException(); } + + /// + public override bool CanRead => true; + + /// + public override bool CanSeek => true; + + /// + public override bool CanWrite => false; + + /// + public override void Flush() { } + + /// + public override long Position { get; set; } + + /// + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + Position = offset; + break; + case SeekOrigin.Current: + Position += offset; + break; + case SeekOrigin.End: + Position = Length - offset; + break; + default: + throw new ArgumentOutOfRangeException(nameof(origin), origin, null); + } + + return Position; + } + + /// + public override void Write(byte[] buffer, int offset, int count) { throw new NotSupportedException(); } + + /// + public override int Read(byte[] buffer, int offset, int count) + { + return ReadAsync(buffer, offset, count).ConfigureAwait(false).GetAwaiter().GetResult(); + } + + /// + public override async Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken cancel) + { + var block = await GetBlockAsync(Position, cancel).ConfigureAwait(false); + var k = Math.Min(count, block.Count); + if (k <= 0) + { + return k; + } + + Array.Copy(block.Array ?? throw new NullReferenceException(), block.Offset, buffer, offset, k); + Position += k; + + return k; + } + + private BlockInfo _currentBlock; + private byte[] _currentData; + + private async Task> GetBlockAsync(long position, CancellationToken cancel) + { + if (position >= Length) + { + return new ArraySegment(); + } + + var need = blocks.Last(b => b.Position <= position); + if (need != _currentBlock) + { + var stream = await UnixFs.CreateReadStreamAsync(need.Id, BlockService, KeyChain, cancel) + .ConfigureAwait(false); + _currentBlock = need; + _currentData = new byte[stream.Length]; + for (int i = 0, n; i < stream.Length; i += n) + { + n = await stream.ReadAsync(_currentData, i, (int) stream.Length - i, cancel); + } + } + + var offset = (int) (position - _currentBlock.Position); + return new ArraySegment(_currentData, offset, _currentData.Length - offset); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/UnixFs/DataMessage.cs b/src/Catalyst.Core.Modules.Dfs/UnixFs/DataMessage.cs new file mode 100644 index 0000000000..3dfc7de99e --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/UnixFs/DataMessage.cs @@ -0,0 +1,134 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using ProtoBuf; + +#pragma warning disable 0649 // disable warning about unassigned fields +#pragma warning disable 0169 // disable warning about unassigned fields + +namespace Catalyst.Core.Modules.Dfs.UnixFs +{ + /// + /// Specifies the type of data. + /// + public enum DataType + { + /// + /// Raw data + /// + Raw = 0, + + /// + /// A directory of files. + /// + Directory = 1, + + /// + /// A file. + /// + File = 2, + + /// + /// Metadata (NYI) + /// + Metadata = 3, + + /// + /// Symbolic link (NYI) + /// + Symlink = 4, + + /// + /// NYI + /// + HamtShard = 5 + }; + + /// + /// The ProtoBuf data that is stored in a DAG. + /// + [ProtoContract] + internal sealed class DataMessage + { + /// + /// The type of data. + /// + [ProtoMember(1, IsRequired = true)] internal DataType Type; + + /// + /// The data. + /// + [ProtoMember(2, IsRequired = false)] internal byte[] Data; + + /// + /// The file size. + /// + [ProtoMember(3, IsRequired = false)] + public ulong? FileSize; + + /// + /// The file size of each block. + /// + [ProtoMember(4, IsRequired = false)] + public ulong[] BlockSizes; + +#pragma warning disable 0649 // disable warning about unassinged fields + /// + /// NYI + /// + [ProtoMember(5, IsRequired = false)] + public ulong? HashType; + +#pragma warning disable 0649 // disable warning about unassinged fields + /// + /// NYI + /// + [ProtoMember(6, IsRequired = false)] + public ulong? Fanout; + } +} + +/* + *module.exports = `message Data + { + enum DataType + { + Raw = 0; + Directory = 1; + File = 2; + Metadata = 3; + Symlink = 4; + HAMTShard = 5; + } + required DataType Type = 1; + optional bytes Data = 2; + optional uint64 filesize = 3; + repeated uint64 blocksizes = 4; + optional uint64 hashType = 5; + optional uint64 fanout = 6; +} +message Metadata +{ + required string MimeType = 1; +} +*/ diff --git a/src/Catalyst.Core.Modules.Dfs/UnixFs/SizeChunker.cs b/src/Catalyst.Core.Modules.Dfs/UnixFs/SizeChunker.cs new file mode 100644 index 0000000000..c9c895c7b5 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/UnixFs/SizeChunker.cs @@ -0,0 +1,193 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.Dag; +using ProtoBuf; + +namespace Catalyst.Core.Modules.Dfs.UnixFs +{ + /// + /// Chunks a data stream into data blocks based upon a size. + /// + public class SizeChunker + { + /// + /// Performs the chunking. + /// + /// + /// The data source. + /// + /// + /// A name for the data. + /// + /// + /// The options when adding data to the IPFS file system. + /// + /// + /// The destination for the chunked data block(s). + /// + /// + /// Used to protect the chunked data blocks(s). + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the sequence of file system nodes of the added data blocks. + /// + public async Task> ChunkAsync(Stream stream, + string name, + AddFileOptions options, + IBlockApi blockService, + IKeyApi keyChain, + CancellationToken cancel) + { + var protecting = !string.IsNullOrWhiteSpace(options.ProtectionKey); + List nodes = new(); + var chunkSize = options.ChunkSize; + var chunk = new byte[chunkSize]; + var chunking = true; + var totalBytes = 0UL; + + while (chunking) + { + // Get an entire chunk. + var length = 0; + while (length < chunkSize) + { + var n = await stream.ReadAsync(chunk, length, chunkSize - length, cancel).ConfigureAwait(false); + if (n < 1) + { + chunking = false; + break; + } + + length += n; + totalBytes += (uint) n; + } + + // Only generate empty block, when the stream is empty. + if (length != 0 || nodes.Count <= 0) + { + options.Progress?.Report(new TransferProgress + { + Name = name, + Bytes = totalBytes + }); + + // if protected data, then get CMS structure. + if (protecting) + { + // TODO: Inefficent to copy chunk, use ArraySegment in DataMessage.Data + var plain = new byte[length]; + Array.Copy(chunk, plain, length); + var cipher = await keyChain.CreateProtectedDataAsync(options.ProtectionKey, plain, cancel) + .ConfigureAwait(false); + var cid = await blockService.PutAsync( + cipher, + "cms", + options.Hash, + options.Encoding, + options.Pin, + cancel).ConfigureAwait(false); + nodes.Add(new UnixFsNode + { + Id = cid, + Size = length, + DagSize = cipher.Length, + Links = UnixFsLink.None + }); + } + else if (options.RawLeaves) + { + // TODO: Inefficent to copy chunk, use ArraySegment in DataMessage.Data + var data = new byte[length]; + Array.Copy(chunk, data, length); + var cid = await blockService.PutAsync( + data, + "raw", + options.Hash, + options.Encoding, + options.Pin, + cancel).ConfigureAwait(false); + nodes.Add(new UnixFsNode + { + Id = cid, + Size = length, + DagSize = length, + Links = UnixFsLink.None + }); + } + else + { + // Build the DAG. + var dm = new DataMessage + { + Type = DataType.File, + FileSize = (ulong) length + }; + if (length > 0) + { + // TODO: Inefficent to copy chunk, use ArraySegment in DataMessage.Data + var data = new byte[length]; + Array.Copy(chunk, data, length); + dm.Data = data; + } + + MemoryStream pb = new(); + Serializer.Serialize(pb, dm); + DagNode dag = new(pb.ToArray(), null, options.Hash); + + // Save it. + dag.Id = await blockService.PutAsync( + dag.ToArray(), + multiHash: options.Hash, + encoding: options.Encoding, + pin: options.Pin, + cancel: cancel).ConfigureAwait(false); + + var node = new UnixFsNode + { + Id = dag.Id, + Size = length, + DagSize = dag.Size, + Links = UnixFsLink.None + }; + nodes.Add(node); + } + } + } + + return nodes; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/UnixFs/UnixFs.cs b/src/Catalyst.Core.Modules.Dfs/UnixFs/UnixFs.cs new file mode 100644 index 0000000000..2e4a76d510 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/UnixFs/UnixFs.cs @@ -0,0 +1,136 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Keystore; +using Catalyst.Core.Lib.Dag; +using Lib.P2P; +using ProtoBuf; + +namespace Catalyst.Core.Modules.Dfs.UnixFs +{ + /// + /// Support for the *nix file system. + /// + internal class UnixFs + { + private static readonly byte[] EmptyData = new byte[0]; + + /// + /// Creates a stream that can read the supplied . + /// + /// + /// The identifier of some content. + /// + /// + /// The source of the cid's data. + /// + /// + /// Used to decypt the protected data blocks. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// a that produces the content of the . + /// + /// + /// The id's is used to determine how to read + /// the conent. + /// + public static Task CreateReadStreamAsync(Cid id, + IBlockApi blockService, + IKeyApi keyChain, + CancellationToken cancel) + { + switch (id.ContentType) + { + // TODO: A content-type registry should be used. + case "dag-pb": + return CreateDagProtoBufStreamAsync(id, blockService, keyChain, cancel); + case "raw": + return CreateRawStreamAsync(id, blockService, cancel); + case "cms": + return CreateCmsStreamAsync(id, blockService, keyChain, cancel); + default: + throw new NotSupportedException($"Cannot read content type '{id.ContentType}'."); + } + } + + private static async Task CreateRawStreamAsync(Cid id, + IBlockApi blockService, + CancellationToken cancel) + { + var block = await blockService.GetAsync(id, cancel).ConfigureAwait(false); + return block.DataStream; + } + + private static async Task CreateDagProtoBufStreamAsync(Cid id, + IBlockApi blockService, + IKeyApi keyChain, + CancellationToken cancel) + { + var block = await blockService.GetAsync(id, cancel).ConfigureAwait(false); + DagNode dag = new(block.DataStream); + var dm = Serializer.Deserialize(dag.DataStream); + + if (dm.Type != DataType.File) + { + throw new Exception($"'{id.Encode()}' is not a file."); + } + + if (dm.Fanout.HasValue) + { + throw new NotImplementedException("files with a fanout"); + } + + // Is it a simple node? + if (dm.BlockSizes == null && !dm.Fanout.HasValue) + { + return new MemoryStream(buffer: dm.Data ?? EmptyData, writable: false); + } + + if (dm.BlockSizes != null) + { + return new ChunkedStream(blockService, keyChain, dag); + } + + throw new Exception($"Cannot determine the file format of '{id}'."); + } + + private static async Task CreateCmsStreamAsync(Cid id, + IBlockApi blockService, + IKeyApi keyChain, + CancellationToken cancel) + { + var block = await blockService.GetAsync(id, cancel).ConfigureAwait(false); + var plain = await keyChain.ReadProtectedDataAsync(block.DataBytes, cancel).ConfigureAwait(false); + return new MemoryStream(plain, false); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/UnixFs/UnixFsLink.cs b/src/Catalyst.Core.Modules.Dfs/UnixFs/UnixFsLink.cs new file mode 100644 index 0000000000..9bf2956674 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/UnixFs/UnixFsLink.cs @@ -0,0 +1,48 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Dfs; +using Lib.P2P; + +namespace Catalyst.Core.Modules.Dfs.UnixFs +{ + /// + /// A link to another in the IPFS Unix File System. + /// + public class UnixFsLink : IFileSystemLink + { + /// + /// An empty set of links. + /// + public static readonly UnixFsLink[] None = new UnixFsLink[0]; + + /// + public string Name { get; set; } + + /// + public Cid Id { get; set; } + + /// + public long Size { get; set; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/UnixFs/UnixFsNode.cs b/src/Catalyst.Core.Modules.Dfs/UnixFs/UnixFsNode.cs new file mode 100644 index 0000000000..e1e3808e1f --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/UnixFs/UnixFsNode.cs @@ -0,0 +1,86 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using Catalyst.Abstractions.Dfs; +using Lib.P2P; + +namespace Catalyst.Core.Modules.Dfs.UnixFs +{ + /// + /// A node in the IPFS Unix File System. + /// + /// + /// A FileSystemNode is either a directory or a file + /// + /// A directory's is a sequence of files/directories + /// belonging to the directory. + /// + /// + public class UnixFsNode : IFileSystemNode + { + /// + public bool IsDirectory { get; set; } + + /// + public IEnumerable Links { get; set; } + + /// + public byte[] DataBytes { get; set; } + + /// + public Stream DataStream { get; set; } + + /// + public Cid Id { get; set; } + + /// + public long Size { get; set; } + + /// + /// The name of the node. + /// + /// + /// Relative to the containing directory. Defaults to "". + /// + public string Name { get; set; } = String.Empty; + + /// + /// The serialised DAG size. + /// + public long DagSize { get; set; } + + /// + public IFileSystemLink ToLink(string name = "") + { + return new UnixFsLink + { + Name = String.IsNullOrWhiteSpace(name) ? Name : name, + Id = Id, + Size = DagSize + }; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BitSwapController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BitSwapController.cs new file mode 100644 index 0000000000..3023eddabd --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BitSwapController.cs @@ -0,0 +1,105 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Lib.P2P; +using Microsoft.AspNetCore.Mvc; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Data trading module for IPFS. Its purpose is to request blocks from and + /// send blocks to other peers in the network. + /// + /// + /// Bitswap has two primary jobs (1) Attempt to acquire blocks from the network that + /// have been requested by the client and (2) Judiciously(though strategically) + /// send blocks in its possession to other peers who want them. + /// + public sealed class BitSwapController : DfsController + { + /// + /// Creates a new controller. + /// + public BitSwapController(IDfsService dfs) : base(dfs) { } + + /// + /// The blocks that are needed by a peer. + /// + /// + /// A peer ID or empty for self. + /// + [HttpGet] [HttpPost] [Route("bitswap/wantlist")] + public async Task Wants(string arg) + { + var peer = string.IsNullOrEmpty(arg) ? null : new MultiHash(arg); + var cids = await DfsService.BitSwapApi.WantsAsync(peer, Cancel); + return new BitSwapWantsDto + { + Keys = cids.Select(cid => new BitSwapLinkDto + { + Link = cid + }).ToList() + }; + } + + /// + /// Remove the CID from the want list. + /// + /// + /// The CID that is no longer needed. + /// + [HttpGet] [HttpPost] [Route("bitswap/unwant")] + public void Unwants(string arg) { DfsService.BitSwapApi.UnWant(arg, Cancel); } + + /// + /// The blocks that are needed by a peer. + /// + /// + /// A peer ID. + /// + [HttpGet] [HttpPost] [Route("bitswap/ledger")] + public BitSwapLedgerDto Ledger(string arg) + { + var peer = new Peer + { + Id = arg + }; + var ledger = DfsService.BitSwapApi.GetBitSwapLedger(peer, Cancel); + return new BitSwapLedgerDto + { + Peer = ledger.Peer.Id.ToBase58(), + Exchanged = ledger.BlocksExchanged, + Recv = ledger.DataReceived, + Sent = ledger.DataSent, + Value = ledger.DebtRatio + }; + } + + // "bitswap/stat" is handled by the StatsController. + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BlockController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BlockController.cs new file mode 100644 index 0000000000..367264b370 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BlockController.cs @@ -0,0 +1,166 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Lib.P2P; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Manages IPFS blocks. + /// + /// + /// An IPFS Block is a byte sequence that represents an IPFS Object + /// (i.e. serialized byte buffers). It is useful to talk about them as "blocks" in Bitswap + /// and other things that do not care about what is being stored. + /// + /// It is also possible to store arbitrary stuff using ipfs block put/get as the API + /// does not check for proper IPFS Object formatting. + /// + /// + /// This may be very good or bad, we haven't decided yet 😄 + /// + /// + public sealed class BlockController : DfsController + { + /// + /// Creates a new controller. + /// + public BlockController(IDfsService dfs) : base(dfs) { } + + /// + /// Get the data of a block. + /// + /// + /// The CID of the block. + /// + [HttpGet, HttpPost, Route("block/get")] + [Produces("application/octet-stream")] + public async Task Get(string arg) + { + var block = await DfsService.BlockApi.GetAsync(arg, Cancel); + Immutable(); + return File(block.DataStream, "application/octet-stream", arg, null, ETag(block.Id)); + } + + /// + /// Get the stats of a block. + /// + /// + /// The CID of the block. + /// + [HttpGet, HttpPost, Route("block/stat")] + public async Task Stats(string arg) + { + var info = await DfsService.BlockApi.StatAsync(arg, Cancel); + if (info == null) + { + throw new KeyNotFoundException($"Block '{arg}' does not exist."); + } + + Immutable(); + return new BlockStatsDto + { + Key = info.Id, Size = info.Size + }; + } + + /// + /// Add a block to the local store. + /// + /// + /// multipart/form-data. + /// + /// + /// The base encoding algorithm. + /// + /// + /// The content type. + /// + /// + /// The hashing algorithm. + /// + [HttpPost("block/put")] + public async Task Put(IFormFile file, + string format = Cid.DefaultContentType, + string mhtype = MultiHash.DefaultAlgorithmName, + [ModelBinder(Name = "cid-base")] string cidBase = MultiBase.DefaultAlgorithmName) + { + if (file == null) + { + throw new ArgumentNullException(nameof(file)); + } + + await using (var data = file.OpenReadStream()) + { + var cid = await DfsService.BlockApi.PutAsync( + data, + format, + encoding: cidBase, + pin: false, + cancel: Cancel); + return new KeyDto {Key = cid}; + } + } + + /// + /// Remove a block from the local store. + /// + /// + /// The CID of the block. + /// + /// + /// If true, do not return an error when the block does + /// not exist. + /// + [HttpGet, HttpPost, Route("block/rm")] + public async Task Remove(string arg, + bool force = false) + { + var cid = await DfsService.BlockApi.RemoveAsync(arg, true, Cancel); + HashDto dto = new(); + if (cid == null && !force) + { + dto.Hash = arg; + dto.Error = "block not found"; + } + else if (cid == null) + { + return null; + } + else + { + dto.Hash = cid; + } + + return dto; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BlockRepositoryController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BlockRepositoryController.cs new file mode 100644 index 0000000000..80f69abd99 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BlockRepositoryController.cs @@ -0,0 +1,72 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Microsoft.AspNetCore.Mvc; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Manages all the blocks in teh repository. + /// + public sealed class BlockRepositoryController : DfsController + { + /// + /// Creates a new controller. + /// + public BlockRepositoryController(IDfsService dfs) : base(dfs) { } + + /// + /// Garbage collection. + /// + [HttpGet, HttpPost, Route("repo/gc")] + public Task GarbageCollection() { return DfsService.BlockRepositoryApi.RemoveGarbageAsync(Cancel); } + + /// + /// Get repository information. + /// + [HttpGet, HttpPost, Route("repo/stat")] + public Task Statistics() { return DfsService.BlockRepositoryApi.StatisticsAsync(Cancel); } + + /// + /// Verify that the blocks are not corrupt. + /// + [HttpGet, HttpPost, Route("repo/verify")] + public Task Verify() { return DfsService.BlockRepositoryApi.VerifyAsync(Cancel); } + + /// + /// Get repository information. + /// + [HttpGet, HttpPost, Route("repo/version")] + public async Task Version() + { + return new VersionBlockRepositoryDto + { + Version = await DfsService.BlockRepositoryApi.VersionAsync(Cancel) + }; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BootstrapController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BootstrapController.cs new file mode 100644 index 0000000000..54b366eb65 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/BootstrapController.cs @@ -0,0 +1,134 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Microsoft.AspNetCore.Mvc; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Manages the list of initial peers. + /// + /// + /// The API manipulates the "bootstrap list", which contains + /// the addresses of the bootstrap nodes. These are the trusted peers from + /// which to learn about other peers in the network. + /// + public sealed class BootstrapController : DfsController + { + /// + /// Creates a new controller. + /// + public BootstrapController(IDfsService dfs) : base(dfs) { } + + /// + /// List all the bootstrap peers. + /// + [HttpGet, HttpPost, Route("bootstrap/list")] + public async Task List() + { + var peers = await DfsService.BootstrapApi.ListAsync(Cancel); + return new BootstrapPeersDto + { + Peers = peers.Select(peer => peer.ToString()) + }; + } + + /// + /// Remove all the bootstrap peers. + /// + [HttpGet, HttpPost, Route("bootstrap/rm/all")] + public async Task RemoveAll() { await DfsService.BootstrapApi.RemoveAllAsync(Cancel); } + + /// + /// Add the default bootstrap peers. + /// + [HttpGet, HttpPost, Route("bootstrap/add/default")] + public async Task AddDefaults() + { + var peers = await DfsService.BootstrapApi.AddDefaultsAsync(Cancel); + return new BootstrapPeersDto + { + Peers = peers.Select(peer => peer.ToString()) + }; + } + + /// + /// Add a bootstrap peer. + /// + /// + /// The multiaddress of the peer. + /// + /// + /// If true, add all the default bootstrap peers. + /// + [HttpGet, HttpPost, Route("bootstrap/add")] + public async Task Add(string arg, + bool @default = false) + { + if (@default) + { + var peers = await DfsService.BootstrapApi.AddDefaultsAsync(Cancel); + return new BootstrapPeersDto + { + Peers = peers.Select(p => p.ToString()) + }; + } + + var peer = await DfsService.BootstrapApi.AddAsync(arg, Cancel); + return new BootstrapPeersDto + { + Peers = new[] {peer?.ToString()} + }; + } + + /// + /// Remove a bootstrap peer. + /// + /// + /// The multiaddress of the peer. + /// + /// + /// If true, remove all the bootstrap peers. + /// + [HttpGet, HttpPost, Route("bootstrap/rm")] + public async Task Remove(string arg, + bool all = false) + { + if (all) + { + await DfsService.BootstrapApi.RemoveAllAsync(Cancel); + return new BootstrapPeersDto {Peers = new string[0]}; + } + + var peer = await DfsService.BootstrapApi.RemoveAsync(arg, Cancel); + return new BootstrapPeersDto + { + Peers = new[] {peer?.ToString()} + }; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/ConfigController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/ConfigController.cs new file mode 100644 index 0000000000..a8e2af00dd --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/ConfigController.cs @@ -0,0 +1,126 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Manages the IPFS Configuration. + /// + public sealed class ConfigController : DfsController + { + /// + /// Creates a new controller. + /// + public ConfigController(IDfsService dfs) : base(dfs) { } + + /// + /// Get all the configuration settings. + /// + [HttpGet, HttpPost, Route("config/show")] + public Task GetAllKeys() { return DfsService.ConfigApi.GetAsync(Cancel); } + + /// + /// Gets or sets the configuration setting. + /// + /// + /// The configuration setting key and possibly its value. + /// + /// + /// Indicates that the value is JSON. + /// + /// + [HttpGet, HttpPost, Route("config")] + public async Task Key(string[] arg, + bool json = false) + { + switch (arg.Length) + { + case 1: + { + var value = await DfsService.ConfigApi.GetAsync(arg[0], Cancel); + return new ConfigDetailDto + { + Key = arg[0], + Value = value + }; + } + case 2 when json: + { + var value = JToken.Parse(arg[1]); + await DfsService.ConfigApi.SetAsync(arg[0], value, Cancel); + return new ConfigDetailDto + { + Key = arg[0], + Value = value + }; + } + case 2: + await DfsService.ConfigApi.SetAsync(arg[0], arg[1], Cancel); + return new ConfigDetailDto + { + Key = arg[0], + Value = arg[1] + }; + default: + throw new FormatException("Too many arg values."); + } + } + + /// + /// Replace all the configuration settings. + /// + /// + /// The new configuration settings. + /// + [HttpGet, HttpPost, Route("config/replace")] + public async Task Replace(IFormFile file) + { + if (file == null) + { + throw new ArgumentNullException(nameof(file)); + } + + await using (var stream = file.OpenReadStream()) + { + using (StreamReader text = new(stream)) + { + using (JsonTextReader reader = new(text)) + { + var json = await JObject.LoadAsync(reader); + await DfsService.ConfigApi.ReplaceAsync(json); + } + } + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DagController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DagController.cs new file mode 100644 index 0000000000..0341a7cba3 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DagController.cs @@ -0,0 +1,117 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using MultiFormats; +using Newtonsoft.Json; +using Newtonsoft.Json.Linq; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Manages the IPLD (linked data) Directed Acrylic Graph. + /// + public class DagController : DfsController + { + /// + /// Creates a new controller. + /// + public DagController(IDfsService dfs) : base(dfs) { } + + /// + /// Resolve a reference (NYI). + /// + [HttpGet, HttpPost, Route("dag/resolve")] // TODO + public Task Resolve(string arg) + { + throw new NotImplementedException("Resolving a dag reference is not implemented."); + } + + /// + /// Gets the content of some linked data. + /// + /// + /// A path, such as "cid", "/ipfs/cid/" or "cid/a". + /// + [HttpGet, HttpPost, Route("dag/get")] + public async Task Get(string arg) { return await DfsService.DagApi.GetAsync(arg, Cancel); } + + /// + /// Add some linked data. + /// + /// + /// multipart/form-data. + /// + /// + /// The base encoding algorithm. + /// + /// + /// The content type. + /// + /// + /// The hashing algorithm. + /// + /// + /// Pin the linked data. + /// + [HttpPost("dag/put")] + public async Task Put(IFormFile file, + string format = "dag-cbor", + string hash = MultiHash.DefaultAlgorithmName, + bool pin = true, + [ModelBinder(Name = "cid-base")] string cidBase = MultiBase.DefaultAlgorithmName) + { + if (file == null) + { + throw new ArgumentNullException(nameof(file)); + } + + await using (var stream = file.OpenReadStream()) + { + using (StreamReader sr = new(stream)) + { + using (JsonTextReader tr = new(sr)) + { + JsonSerializer serializer = new(); + JObject json = (JObject) serializer.Deserialize(tr); + + var cid = await DfsService.DagApi.PutAsync( + json, + contentType: format, + multiHash: hash, + encoding: cidBase, + pin: false, + cancel: Cancel); + return new LinkedDataCidDto {Cid = new LinkedDataDto {Link = cid}}; + } + } + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DfsController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DfsController.cs new file mode 100644 index 0000000000..8eb9a3ccac --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DfsController.cs @@ -0,0 +1,121 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Text; +using System.Threading; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Filter; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Response; +using Lib.P2P; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Primitives; +using Microsoft.Net.Http.Headers; +using Newtonsoft.Json; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// A base controller for Dfs HTTP API. + /// + /// + /// Any unhandled exceptions are translated into an by the + /// . + /// + [Route("api/v0")] + [Produces("application/json")] + [ApiExceptionFilter] + public abstract class DfsController : Controller + { + /// + /// Creates a new instance of the controller. + /// + /// + /// An implementation of the Dfs Core API. + /// + protected DfsController(ICoreApi dfs) { DfsService = dfs; } + + /// + /// An implementation of the Dfs Core API. + /// + protected ICoreApi DfsService { get; } + + /// + /// Notifies when the request is cancelled. + /// + /// + /// See + /// + /// + /// There is no timeout for a request, because of the + /// distributed nature of Dfs. + /// + protected CancellationToken Cancel => HttpContext.RequestAborted; + + /// + /// Declare that the response is immutable and should be cached forever. + /// @TODO lets look if there is any type of response interrceptor + /// in the request lifecycle so we can force outgoing response to be immutable rather than adhock calling it + /// + protected void Immutable() + { + Response.Headers.Add("cache-control", new StringValues("public, max-age=31536000, immutable")); + } + + /// + /// Get the strong ETag for a CID. + /// + protected EntityTagHeaderValue ETag(Cid id) + { + return new EntityTagHeaderValue(new StringSegment("\"" + id + "\""), isWeak: false); + } + + /// + /// Immediately send the JSON. + /// + /// + /// The object to send to the requester. + /// + /// + /// Immediately sends the Line Delimited JSON (LDJSON) representation + /// of to the requestor. + /// + protected void StreamJson(object o) + { + if (!Response.HasStarted) + { + Response.StatusCode = 200; + Response.ContentType = "application/json"; + } + + using (StringWriter sw = new()) + { + JsonSerializer.Create().Serialize(sw, o); + sw.Write('\n'); + var bytes = Encoding.UTF8.GetBytes(sw.ToString()); + Response.Body.Write(bytes, 0, bytes.Length); + Response.Body.Flush(); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DhtController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DhtController.cs new file mode 100644 index 0000000000..bb2a2f7310 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DhtController.cs @@ -0,0 +1,107 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Microsoft.AspNetCore.Mvc; + +// TODO: need MultiAddress.WithOutPeer (should be in DFS code) + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Distributed Hash Table. + /// + /// + /// The DHT is a place to store, not the value, but pointers to peers who have + /// the actual value. + /// + public sealed class DhtController : DfsController + { + /// + /// Creates a new controller. + /// + public DhtController(ICoreApi dfs) : base(dfs) { } + + /// + /// Query the DHT for all of the multiaddresses associated with a Peer ID. + /// + /// + /// The peer ID to find. + /// + /// + /// Information about the peer. + /// + [HttpGet, HttpPost, Route("dht/findpeer")] + public async Task FindPeer(string arg) + { + var peer = await DfsService.DhtApi.FindPeerAsync(arg, Cancel); + return new DhtPeerDto + { + Id = peer.Id.ToBase58(), + Responses = new[] + { + new DhtPeerResponseDto + { + Id = peer.Id.ToBase58(), + Addrs = peer.Addresses.Select(a => a.WithoutPeerId().ToString()) + } + } + }; + } + + /// + /// Find peers in the DHT that can provide a specific value, given a key. + /// + /// + /// The CID key, + /// + /// + /// The maximum number of providers to find. + /// + /// + /// Information about the peer providers. + /// + [HttpGet, HttpPost, Route("dht/findprovs")] + public async Task> FindProviders(string arg, + [ModelBinder(Name = "num-providers")] int limit = 20) + { + var peers = await DfsService.DhtApi.FindProvidersAsync(arg, limit, null, Cancel); + return peers.Select(peer => new DhtPeerDto + { + Id = peer.Id.ToBase58(), // TODO: should be the peer ID that answered the query + Responses = new[] + { + new DhtPeerResponseDto + { + Id = peer.Id.ToBase58(), + Addrs = peer.Addresses.Select(a => a.WithoutPeerId().ToString()) + } + } + }); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DnsController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DnsController.cs new file mode 100644 index 0000000000..f85954bb55 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/DnsController.cs @@ -0,0 +1,66 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Microsoft.AspNetCore.Mvc; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// DNS mapping to IPFS. + /// + /// + /// Multihashes are hard to remember, but domain names are usually easy to + /// remember. To create memorable aliases for multihashes, DNS TXT + /// records can point to other DNS links, IPFS objects, IPNS keys, etc. + /// + public sealed class DnsController : DfsController + { + /// + /// Creates a new controller. + /// + public DnsController(IDfsService dfs) : base(dfs) { } + + /// + /// Resolve a domain name to an IPFS path. + /// + /// + /// A domain name, such as "ipfs.io". + /// + /// + /// Resolve until the result is not a DNS link. Defaults to false. + /// + /// + /// The resolved IPFS path, such as + /// /ipfs/QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao. + /// + [HttpGet, HttpPost, Route("dns")] + public async Task Get(string arg, bool recursive = false) + { + var path = await DfsService.DnsApi.ResolveAsync(arg, recursive, Cancel); + return new PathDto(path); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/FileSystemController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/FileSystemController.cs new file mode 100644 index 0000000000..9f7879ffdf --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/FileSystemController.cs @@ -0,0 +1,213 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Lib.P2P; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.Net.Http.Headers; +using MultiFormats; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// DNS mapping to IPFS. + /// + /// + /// Multihashes are hard to remember, but domain names are usually easy to + /// remember. To create memorable aliases for multihashes, DNS TXT + /// records can point to other DNS links, IPFS objects, IPNS keys, etc. + /// + public sealed class FileSystemController : DfsController + { + /// + /// Creates a new controller. + /// + public FileSystemController(IDfsService dfs) : base(dfs) { } + + /// + /// Get the contents of a file or directory. + /// + /// + /// A path to an existing file, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" + /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" + /// + /// + /// Offset into the file. + /// + /// + /// Number of bytes to read. + /// + [HttpGet, HttpPost, Route("cat")] + [Produces("application/octet-stream")] + public async Task Cat(string arg, + long offset = 0, + long length = 0) + { + EntityTagHeaderValue etag = null; + var path = await DfsService.NameApi.ResolveAsync(arg, true, false, Cancel); + var cid = Cid.Decode(path.Substring(6)); // remove leading "/ipfs/" + + // Use an etag if the path is IPFS or CID. + if (arg.StartsWith("/ipfs/") || arg[0] != '/') + { + etag = ETag(cid); + Immutable(); + } + + // Use the last part of the path as the download filename + var filename = arg.Split('/').Last(); + var stream = await DfsService.UnixFsApi.ReadFileAsync(cid, offset, length, Cancel); + return File(stream, "application/octet-stream", filename, null, etag); + } + + /// + /// Get the object as a TAR file. + /// + /// + /// A path to an existing file or directory. + /// + /// + /// If true, generate gzipped TAR. + /// + [HttpGet, HttpPost, Route("get")] + [Produces("application/tar")] + public async Task Get(string arg, bool compress = false) + { + var tar = await DfsService.UnixFsApi.GetAsync(arg, compress, Cancel); + Response.ContentType = "application/tar"; + Response.Headers.Add("X-Stream-Output", "1"); + Response.Headers.Add("X-Content-Length", "4"); + Response.StatusCode = 200; + + await tar.CopyToAsync(Response.Body); + await Response.Body.FlushAsync(); + } + + /// + /// Get information on the file or directory. + /// + /// + /// A path to an existing file, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" + /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" + /// + [HttpGet, HttpPost, Route("file/ls")] + public async Task Stat(string arg) + { + var node = await DfsService.UnixFsApi.ListFileAsync(arg, Cancel); + var dto = new FileSystemDetailsDto + { + Arguments = new Dictionary(), + Objects = new Dictionary() + }; + dto.Arguments[arg] = node.Id; + dto.Objects[node.Id] = new FileSystemDetailDto + { + Hash = node.Id, + Size = node.Size, + Type = node.IsDirectory ? "Directory" : "File", + Links = node.Links + .Select(link => new FileSystemLinkDto + { + Hash = link.Id, + Name = link.Name, + Size = link.Size + }) + .ToArray() + }; + return dto; + } + + /// + /// Add a file. + /// + [HttpGet, HttpPost, Route("add")] + public async Task Add(IFormFile file, + string hash = MultiHash.DefaultAlgorithmName, + [ModelBinder(Name = "cid-base")] string cidBase = MultiBase.DefaultAlgorithmName, + [ModelBinder(Name = "only-hash")] bool onlyHash = false, + string chunker = null, + bool pin = false, + [ModelBinder(Name = "raw-leaves")] bool rawLeaves = false, + bool trickle = false, + [ModelBinder(Name = "wrap-with-directory")] + bool wrap = false, + string protect = null, + bool progress = true) + { + if (file == null) + { + throw new ArgumentNullException(nameof(file)); + } + + var options = new AddFileOptions + { + Encoding = cidBase, + Hash = hash, + OnlyHash = onlyHash, + Pin = pin, + RawLeaves = rawLeaves, + Trickle = trickle, + Wrap = wrap, + ProtectionKey = protect, + }; + if (chunker != null) + { + if (chunker.StartsWith("size-")) + { + options.ChunkSize = int.Parse(chunker.Substring(5), CultureInfo.InvariantCulture); + } + else + { + throw new ArgumentOutOfRangeException(nameof(chunker)); + } + } + + if (progress) + { + options.Progress = new Progress(StreamJson); + } + + // TODO: Accept multiple files. + await using (var stream = file.OpenReadStream()) + { + // TODO: AddAsync returns a list of nodes containing every node added not just the top level. + var node = await DfsService.UnixFsApi.AddAsync(stream, file.FileName, options, Cancel); + StreamJson(new FileSystemNodeDto + { + Name = node.Id, + Hash = node.Id, + Size = node.Size.ToString(CultureInfo.InvariantCulture) + }); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/GenericController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/GenericController.cs new file mode 100644 index 0000000000..cac0243c6f --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/GenericController.cs @@ -0,0 +1,97 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +// using System; +// using System.Collections.Generic; +// using System.Threading.Tasks; +// using Catalyst.Abstractions.Dfs.CoreApi; +// using Microsoft.AspNetCore.Mvc; +// using MultiFormats; +// +// namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +// { +// /// +// /// Some miscellaneous methods. +// /// +// public class GenericController : IpfsController +// { +// /// +// /// Creates a new instance of the controller. +// /// +// public GenericController(ICoreApi ipfs) : base(ipfs) { } +// +// /// +// /// Information about the peer. +// /// +// /// +// /// The peer's ID or empty for the local peer. +// /// +// [HttpGet, HttpPost, Route("id")] +// public async Task Get(string arg) +// { +// MultiHash id = null; +// if (!String.IsNullOrEmpty(arg)) +// id = arg; +// +// var peer = await IpfsCore.Generic.IdAsync(id, Cancel); +// return new PeerInfoDto(peer); +// } +// +// /// +// /// Version information on the local peer. +// /// +// [HttpGet, HttpPost, Route("version")] +// public async Task> Version() { return await IpfsCore.Generic.VersionAsync(Cancel); } +// +// /// +// /// Resolve a name. +// /// +// /// +// /// The name to resolve. Can be CID + [/path], "/ipfs/..." or +// /// "/ipns/...". +// /// +// /// +// /// Resolve until the result is an IPFS name. Defaults to false. +// /// +// [HttpGet(), HttpPost(), Route("resolve")] +// public async Task Resolve(string arg, bool recursive = false) +// { +// var path = await IpfsCore.Generic.ResolveAsync(arg, recursive, Cancel); +// return new PathDto(path); +// } +// +// /// +// /// Stop the IPFS peer. +// /// +// /// +// [HttpGet, HttpPost, Route("shutdown")] +// public async Task Shutdown() +// { +// await IpfsCore.Generic.ShutdownAsync(); +// +// Program.Shutdown(); +// } +// } +// } + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers {} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/KeyController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/KeyController.cs new file mode 100644 index 0000000000..7ba4f1bdf0 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/KeyController.cs @@ -0,0 +1,156 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Microsoft.AspNetCore.Mvc; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Manages the cryptographic keys. + /// @TODO use Dawn guards rather than if evaluations for arg params in methods + /// + public class KeyController : DfsController + { + /// + /// Creates a new controller. + /// + public KeyController(IDfsService dfs) : base(dfs) { } + + /// + /// List all the keys. + /// + [HttpGet, HttpPost, Route("key/list")] + public async Task List() + { + var keys = await DfsService.KeyApi.ListAsync(Cancel); + return new CryptoKeysDto + { + Keys = keys.Select(key => new CryptoKeyDto + { + Name = key.Name, + Id = key.Id.ToString() + }) + }; + } + + /// + /// Create a new key. + /// + /// + /// The name of the key. + /// + /// + /// "rsa" + /// + /// + /// The key size in bits, if the type requires it. + /// + [HttpGet, HttpPost, Route("key/gen")] + public async Task Create(string arg, + string type, + int size) + { + if (string.IsNullOrWhiteSpace(arg)) + { + throw new ArgumentNullException(nameof(arg), "The key name is required."); + } + + if (string.IsNullOrWhiteSpace(type)) + { + throw new ArgumentNullException(nameof(type), "The key type is required."); + } + + var key = await DfsService.KeyApi.CreateAsync(arg, type, size, Cancel); + return new CryptoKeyDto + { + Name = key.Name, + Id = key.Id.ToString() + }; + } + + /// + /// Remove a key. + /// + /// + /// The name of the key. + /// + [HttpGet, HttpPost, Route("key/rm")] + public async Task Remove(string arg) + { + if (string.IsNullOrWhiteSpace(arg)) + { + throw new ArgumentNullException(nameof(arg), "The key name is required."); + } + + var key = await DfsService.KeyApi.RemoveAsync(arg, Cancel); + CryptoKeysDto dto = new(); + if (key != null) + { + dto.Keys = new[] + { + new CryptoKeyDto + { + Name = key.Name, + Id = key.Id.ToString() + } + }; + } + + return dto; + } + + /// + /// Rename a key. + /// + /// + /// The old and new key name. + /// + [HttpGet, HttpPost, Route("key/rename")] + public async Task Rename(string[] arg) + { + if (arg.Length != 2) + { + throw new ArgumentException("Missing the old and/or new key name."); + } + + var key = await DfsService.KeyApi.RenameAsync(arg[0], arg[1], Cancel); + var dto = new CryptoKeyRenameDto + { + Was = arg[0], + Now = arg[1], + Id = key.Id.ToString() + + // TODO: Overwrite + }; + return dto; + } + + // TODO: import + // TODO: export + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/NameController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/NameController.cs new file mode 100644 index 0000000000..dc527ab086 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/NameController.cs @@ -0,0 +1,107 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Lib.P2P; +using Microsoft.AspNetCore.Mvc; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Manages the IPNS (Interplanetary Name Space). + /// + /// + /// IPNS is a PKI namespace, where names are the hashes of public keys, and + /// the private key enables publishing new(signed) values. The default name + /// is the node's own , + /// which is the hash of its public key. + /// + public sealed class NameController : DfsController + { + /// + /// Creates a new controller. + /// + public NameController(IDfsService dfs) : base(dfs) { } + + /// + /// Resolve a name. + /// + [HttpGet] [HttpPost] [Route("name/resolve")] + public async Task Resolve(string arg, + bool recursive = false, + bool nocache = false) + { + var path = await DfsService.NameApi.ResolveAsync(arg, recursive, nocache, Cancel); + return new PathDto(path); + } + + /// + /// Publish content. + /// + /// + /// The CID or path to the content to publish. + /// + /// + /// Resolve before publishing. + /// + /// + /// The local key name used to sign the content. + /// + /// + /// Duration that the record will be valid for. + /// + [HttpGet] [HttpPost] [Route("name/publish")] + public async Task Publish(string arg, + bool resolve = true, + string key = "self", + string lifetime = "24h") + { + if (string.IsNullOrWhiteSpace(arg)) + { + throw new ArgumentNullException(nameof(arg), "The name is required."); + } + + if (string.IsNullOrWhiteSpace(key)) + { + throw new ArgumentNullException(nameof(arg), "The key name is required."); + } + + if (string.IsNullOrWhiteSpace(lifetime)) + { + throw new ArgumentNullException(nameof(arg), "The lifetime is required."); + } + + var duration = Duration.Parse(lifetime); + var content = await DfsService.NameApi.PublishAsync(arg, resolve, key, duration, Cancel); + return new NamedContentDto + { + Name = content.NamePath, + Value = content.ContentPath + }; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/ObjectController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/ObjectController.cs new file mode 100644 index 0000000000..3835a28d8c --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/ObjectController.cs @@ -0,0 +1,319 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Core.Lib.Dag; +using Lib.P2P; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Stats for an object. + /// + public class ObjectStatDto + { + /// + /// The CID of the object. + /// + public string Hash { set; get; } + + /// + /// Number of links. + /// + public int NumLinks { get; set; } + + /// + /// Size of the links segment. + /// + public long LinksSize { get; set; } + + /// + /// Size of the raw, encoded data. + /// + public long BlockSize { get; set; } + + /// + /// Siz of the data segment. + /// + public long DataSize { get; set; } + + /// + /// Size of object and its references + /// + public long CumulativeSize { get; set; } + } + + /// + /// A link to a file. + /// + public class ObjectLinkDto + { + /// + /// The object name. + /// + public string Name { set; get; } + + /// + /// The CID of the object. + /// + public string Hash { set; get; } + + /// + /// The object size. + /// + public long Size { set; get; } + } + + /// + /// Link details on an object. + /// + public class ObjectLinkDetailDto + { + /// + /// The CID of the object. + /// + public string Hash { set; get; } + + /// + /// Links to other objects. + /// + public IEnumerable Links { set; get; } + } + + /// + /// Data and link details on an object. + /// + public class ObjectDataDetailDto : ObjectLinkDetailDto + { + /// + /// The object data encoded as UTF-8. + /// + public string Data { set; get; } + } + + /// + /// Manages the IPFS Merkle Directed Acrylic Graph. + /// + /// + /// + /// This is being obsoleted by . + /// + /// + public sealed class ObjectController : DfsController + { + /// + /// Creates a new controller. + /// + public ObjectController(ICoreApi dfs) : base(dfs) { } + + /// + /// Create an object from a template. + /// + /// + /// Template name. Must be "unixfs-dir". + /// + [HttpGet] [HttpPost] [Route("object/new")] + private async Task Create(string arg) + { + var node = await DfsService.ObjectApi.NewAsync(arg, Cancel); + Immutable(); + return new ObjectLinkDetailDto + { + Hash = node.Id, + Links = node.Links.Select(link => new ObjectLinkDto + { + Hash = link.Id, + Name = link.Name, + Size = link.Size + }) + }; + } + + /// + /// Store a MerkleDAG node. + /// + /// + /// multipart/form-data. + /// + /// + /// "protobuf" or "json" + /// + /// + /// "text" or "base64" + /// + /// + /// Pin the object. + /// + /// + [HttpPost("object/put")] + private async Task Put(IFormFile file, + string inputenc = "json", + string datafieldenc = "text", + bool pin = false) + { + if (datafieldenc != "text") // TODO + { + throw new NotImplementedException("Only datafieldenc = `text` is allowed."); + } + + IDagNode node; + switch (inputenc) + { + case "protobuf": + await using (var stream = file.OpenReadStream()) + { + DagNode dag = new(stream); + node = await DfsService.ObjectApi.PutAsync(dag, Cancel); + } + + break; + + case "json": // TODO + default: + throw new ArgumentException("inputenc", $"Input encoding '{inputenc}' is not supported."); + } + + if (pin) + { + await DfsService.PinApi.AddAsync(node.Id, false, Cancel); + } + + return new ObjectLinkDetailDto + { + Hash = node.Id, + Links = node.Links.Select(link => new ObjectLinkDto + { + Hash = link.Id, + Name = link.Name, + Size = link.Size + }) + }; + } + + /// + /// Get the data and links of an object. + /// + /// + /// The object's CID. + /// + /// + /// The encoding of the object's data; "text" (default) or "base64". + /// + [ + HttpGet] [HttpPost] [Route("object/get")] + private async Task Get(string arg, + [ModelBinder(Name = "data-encoding")] string dataEncoding) + { + var node = await DfsService.ObjectApi.GetAsync(arg, Cancel); + Immutable(); + var dto = new ObjectDataDetailDto + { + Hash = arg, + Links = node.Links.Select( + link => new ObjectLinkDto {Hash = link.Id, Name = link.Name, Size = link.Size} + ), + Data = dataEncoding switch + { + "base64" => Convert.ToBase64String(node.DataBytes), + "text" => Encoding.UTF8.GetString(node.DataBytes), + _ => Encoding.UTF8.GetString(node.DataBytes) + } + }; + + return dto; + } + + /// + /// Get the links of an object. + /// + /// + /// The object's CID. + /// + [ + HttpGet] [HttpPost] [Route("object/links")] + private async Task Links(string arg) + { + var links = await DfsService.ObjectApi.LinksAsync(arg, Cancel); + Immutable(); + return new ObjectLinkDetailDto + { + Hash = arg, + Links = links.Select(link => new ObjectLinkDto + { + Hash = link.Id, + Name = link.Name, + Size = link.Size + }) + }; + } + + /// + /// Get the object's data. + /// + /// + /// The object's CID or a path. + /// + [ + HttpGet] [HttpPost] [Route("object/data")] + [Produces("text/plain")] + private async Task Data(string arg) + { + var r = await DfsService.NameApi.ResolveAsync(arg, true, false, Cancel); + var cid = Cid.Decode(r.Remove(0, 6)); // strip '/ipfs/'. + var stream = await DfsService.ObjectApi.DataAsync(cid, Cancel); + + return File(stream, "text/plain"); + } + + /// + /// Get the stats of an object. + /// + /// + /// The object's CID. + /// + [ + HttpGet] [HttpPost] [Route("object/stat")] + private async Task Stat(string arg) + { + var info = await DfsService.ObjectApi.StatAsync(arg, Cancel); + Immutable(); + return new ObjectStatDto + { + Hash = arg, + BlockSize = info.BlockSize, + CumulativeSize = info.CumulativeSize, + DataSize = info.DataSize, + LinksSize = info.LinkSize, + NumLinks = info.LinkCount + }; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/PinController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/PinController.cs new file mode 100644 index 0000000000..f8aceb28c2 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/PinController.cs @@ -0,0 +1,95 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Microsoft.AspNetCore.Mvc; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Manage pinned objects (locally stored and permanent). + /// + public class PinController : DfsController + { + /// + /// Creates a new controller. + /// + public PinController(IDfsService dfs) : base(dfs) { } + + /// + /// List the pins. + /// + [HttpGet] [HttpPost] [Route("pin/ls")] + public async Task List() + { + var cids = await DfsService.PinApi.ListAsync(Cancel); + return new PinDetailsDto + { + Keys = cids.ToDictionary(cid => cid.Encode(), cid => new PinDetailDto()) + }; + } + + /// + /// Pin the content. + /// + /// + /// The CID of the content. + /// + /// + /// Recursively pin links of the content. + /// + [HttpGet] [HttpPost] [Route("pin/add")] + public async Task Add(string arg, + bool recursive = true) + { + var cids = await DfsService.PinApi.AddAsync(arg, recursive, Cancel); + return new PinsDto + { + Pins = cids.Select(cid => cid.Encode()) + }; + } + + /// + /// Remove a pin. + /// + /// + /// The CID of the content. + /// + /// + /// Recursively unpin links of the content. + /// + [HttpGet] [HttpPost] [Route("pin/rm")] + public async Task Remove(string arg, + bool recursive = true) + { + var cids = await DfsService.PinApi.RemoveAsync(arg, recursive, Cancel); + return new PinsDto + { + Pins = cids.Select(cid => cid.Encode()) + }; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/PubSubController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/PubSubController.cs new file mode 100644 index 0000000000..d1806c6bf4 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/PubSubController.cs @@ -0,0 +1,123 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Microsoft.AspNetCore.Mvc; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Publishing and subscribing to messages on a topic. + /// + public class PubSubController : DfsController + { + /// + /// Creates a new controller. + /// + public PubSubController(IDfsService dfs) : base(dfs) { } + + /// + /// List all the subscribed topics. + /// + [HttpGet] [HttpPost] [Route("pubsub/ls")] + public async Task List() + { + return new PubSubTopicsDto + { + Strings = await DfsService.PubSubApi.SubscribedTopicsAsync(Cancel) + }; + } + + /// + /// List all the peers associated with the topic. + /// + /// + /// The topic name or null/empty for "all topics". + /// + [HttpGet] [HttpPost] [Route("pubsub/peers")] + public async Task Peers(string arg) + { + var topic = string.IsNullOrEmpty(arg) ? null : arg; + var peers = await DfsService.PubSubApi.PeersAsync(topic, Cancel); + return new PubSubPeersDto + { + Strings = peers.Select(p => p.Id.ToString()) + }; + } + + /// + /// Publish a message to a topic. + /// + /// + /// The first arg is the topic name and second is the message. + /// + [HttpGet] [HttpPost] [Route("pubsub/pub")] + public async Task Publish(string[] arg) + { + if (arg.Length != 2) + { + throw new ArgumentException("Missing topic and/or message."); + } + + var message = arg[1].Select(c => (byte) c).ToArray(); + await DfsService.PubSubApi.PublishAsync(arg[0], message, Cancel); + } + + /// + /// Subscribe to messages on the topic. + /// + /// + /// The topic name. + /// + [HttpGet] [HttpPost] [Route("pubsub/sub")] + public async Task Subscribe(string arg) + { + await DfsService.PubSubApi.SubscribeAsync(arg, message => + { + // Send the published message to the caller. + MessageDto dto = new(message); + StreamJson(dto); + }, Cancel); + + // Send 200 OK to caller; but do not close the stream + // so that published messages can be sent. + Response.ContentType = "application/json"; + Response.StatusCode = 200; + await Response.Body.FlushAsync(); + + // Wait for the caller to cancel. + try + { + await Task.Delay(-1, Cancel); + } + catch (TaskCanceledException) + { + // eat + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/StatsController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/StatsController.cs new file mode 100644 index 0000000000..541bb9ff30 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/StatsController.cs @@ -0,0 +1,86 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Lib.P2P.Transports; +using Microsoft.AspNetCore.Mvc; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// Get the statistics on various IPFS components. + /// + public class StatsController : DfsController + { + /// + /// Creates a new controller. + /// + public StatsController(IDfsService dfs) : base(dfs) { } + + /// + /// Get bandwidth information. + /// + [HttpGet] [HttpPost] [Route("stats/bw")] + public Task Bandwidth() + { + Response.Headers.Add("X-Chunked-Output", "1"); + return DfsService.StatsApi.GetBandwidthStatsAsync(Cancel); + } + + /// + /// Get bitswap information. + /// + [HttpGet] [HttpPost] [Route("stats/bitswap")] [Route("bitswap/stat")] + public StatsBitSwapDto Bitswap() + { + Response.Headers.Add("X-Chunked-Output", "1"); + var data = DfsService.StatsApi.GetBitSwapStats(Cancel); + return new StatsBitSwapDto + { + BlocksReceived = data.BlocksReceived, + BlocksSent = data.BlocksSent, + DataReceived = data.DataReceived, + DataSent = data.DataSent, + DupBlksReceived = data.DupBlksReceived, + DupDataReceived = data.DupDataReceived, + ProvideBufLen = data.ProvideBufLen, + Peers = data.Peers.Select(peer => peer.ToString()), + Wantlist = data.Wantlist.Select(cid => new BitSwapLinkDto {Link = cid}) + }; + } + + /// + /// Get repository information. + /// + [HttpGet] [HttpPost] [Route("stats/repo")] + public Task Repo() + { + Response.Headers.Add("X-Chunked-Output", "1"); + return DfsService.StatsApi.GetRepositoryStatsAsync(Cancel); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/SwamController.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/SwamController.cs new file mode 100644 index 0000000000..a11516c7a6 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Controllers/SwamController.cs @@ -0,0 +1,131 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Dto; +using Microsoft.AspNetCore.Mvc; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Controllers +{ + /// + /// TODO + /// + public class SwarmController : DfsController + { + /// + /// Creates a new controller. + /// + public SwarmController(IDfsService dfs) : base(dfs) { } + + /// + /// Peer addresses. + /// + [HttpGet, HttpPost, Route("swarm/addrs")] + public AddrsDto PeerAddresses() + { + var peers = DfsService.SwarmApi.GetSwarmKnownPeers(Cancel); + AddrsDto dto = new(); + foreach (var peer in peers) + { + dto.Addrs[peer.Id.ToString()] = peer.Addresses + .Select(a => a.ToString()) + .ToList(); + } + + return dto; + } + + /// + /// Connected peers. + /// + [HttpGet, HttpPost, Route("swarm/peers")] + public async Task ConnectedPeers() + { + var peers = await DfsService.SwarmApi.PeersAsync(Cancel); + return new ConnectedPeersDto + { + Peers = peers.Select(peer => new ConnectedPeerDto(peer)).ToArray() + }; + } + + /// + /// List the address filters. + /// + [HttpGet, HttpPost, Route("swarm/filters")] + public async Task ListFilters() + { + var filters = await DfsService.SwarmApi.ListAddressFiltersAsync(persist: false, cancel: Cancel); + return new FiltersDto + { + Strings = filters.Select(f => f.ToString()).ToArray() + }; + } + + /// + /// Add an address filter. + /// + /// + /// A multiaddress. + /// + [HttpGet, HttpPost, Route("swarm/filters/add")] + public async Task AddFilter(string arg) + { + var filter = await DfsService.SwarmApi.AddAddressFilterAsync(arg, persist: false, cancel: Cancel); + return new FiltersDto + { + Strings = filter == null ? new string[0] : new[] {filter.ToString()} + }; + } + + /// + /// Remove an address filter. + /// + /// + /// A multiaddress. + /// + [HttpGet, HttpPost, Route("swarm/filters/rm")] + public async Task RemoveFilter(string arg) + { + var filter = await DfsService.SwarmApi.RemoveAddressFilterAsync(arg, persist: false, cancel: Cancel); + return new FiltersDto + { + Strings = filter == null ? new string[0] : new[] {filter.ToString()} + }; + } + + // [HttpGet, HttpPost, Route("swarm/connect")] + // public Task Connect(string arg) { return IpfsCore.SwarmApi.ConnectAsync(arg, Cancel); } + + /// + /// Disconnect from a peer. + /// + /// + /// The multiaddress of the peer. + /// + [HttpGet, HttpPost, Route("swarm/disconnect")] + public Task Disconnect(string arg) { return DfsService.SwarmApi.DisconnectAsync(arg, Cancel); } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/AddrsDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/AddrsDto.cs new file mode 100644 index 0000000000..e5b41e3deb --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/AddrsDto.cs @@ -0,0 +1,38 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Addresses for peers. + /// + public class AddrsDto + { + /// + /// Addresses for peers. + /// + public Dictionary> Addrs = new Dictionary>(); + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BitSwapLedgerDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BitSwapLedgerDto.cs new file mode 100644 index 0000000000..b5ec074597 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BitSwapLedgerDto.cs @@ -0,0 +1,56 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// The bitswap ledger with another peer. + /// + public sealed class BitSwapLedgerDto + { + /// + /// The peer ID. + /// + internal string Peer; + + /// + /// The debt ratio. + /// + internal double Value; + + /// + /// The number of bytes sent. + /// + internal ulong Sent; + + /// + /// The number of bytes received. + /// + internal ulong Recv; + + /// + /// The number blocks exchanged. + /// + internal ulong Exchanged; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BitSwapLinkDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BitSwapLinkDto.cs new file mode 100644 index 0000000000..d5943710cf --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BitSwapLinkDto.cs @@ -0,0 +1,39 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Newtonsoft.Json; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A link to a CID. + /// + public class BitSwapLinkDto + { + /// + /// The CID. + /// + [JsonProperty(PropertyName = "/")] + public string Link; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BitSwapWantsDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BitSwapWantsDto.cs new file mode 100644 index 0000000000..928bb0aea6 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BitSwapWantsDto.cs @@ -0,0 +1,38 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Outstanding wants. + /// + public sealed class BitSwapWantsDto + { + /// + /// All the links. + /// + internal List Keys; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BlockStatsDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BlockStatsDto.cs new file mode 100644 index 0000000000..40dfe34be1 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BlockStatsDto.cs @@ -0,0 +1,41 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Statistics on a block. + /// + public sealed class BlockStatsDto + { + /// + /// Something like "QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao". + /// + internal string Key; + + /// + /// The size, in bytes, of the block. + /// + internal long Size; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BootstrapPeersDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BootstrapPeersDto.cs new file mode 100644 index 0000000000..23e0491fd9 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/BootstrapPeersDto.cs @@ -0,0 +1,38 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A list of peers. + /// + public sealed class BootstrapPeersDto + { + /// + /// The multiaddress of a peer. + /// + internal IEnumerable Peers; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/ConfigDetailDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/ConfigDetailDto.cs new file mode 100644 index 0000000000..0a30741df9 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/ConfigDetailDto.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Newtonsoft.Json.Linq; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Details on a configuration setting. + /// + public sealed class ConfigDetailDto + { + /// + /// The name of the configuration setting. + /// + internal string Key; + + /// + /// The value of the configuration setting. + /// + internal JToken Value; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/ConnectedPeerDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/ConnectedPeerDto.cs new file mode 100644 index 0000000000..19080e887d --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/ConnectedPeerDto.cs @@ -0,0 +1,59 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Core.Lib.Util; +using Lib.P2P; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Information on a peer. + /// + public class ConnectedPeerDto + { + /// + /// The unique ID of the peer. + /// + public string Peer; + + /// + /// The connected address. + /// + public string Addr; + + /// + /// Avg time to the peer. + /// + public string Latency; + + /// + /// Creates a new peer info. + /// + public ConnectedPeerDto(Peer peer) + { + Peer = peer.Id.ToString(); + Addr = peer.ConnectedAddress?.WithoutPeerId().ToString(); + Latency = peer.Latency == null ? "n/a" : Duration.Stringify(peer.Latency.Value, string.Empty); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/ConnectedPeersDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/ConnectedPeersDto.cs new file mode 100644 index 0000000000..48114406da --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/ConnectedPeersDto.cs @@ -0,0 +1,38 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Information on a peer. + /// + public class ConnectedPeersDto + { + /// + /// The connected peers. + /// + public IEnumerable Peers; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/CryptoKeyDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/CryptoKeyDto.cs new file mode 100644 index 0000000000..4b9bbec942 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/CryptoKeyDto.cs @@ -0,0 +1,41 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A cryptographic key. + /// + public sealed class CryptoKeyDto + { + /// + /// The key's local name. + /// + public string Name { set; get; } + + /// + /// The key's global unique ID. + /// + public string Id { set; get; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/CryptoKeyRenameDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/CryptoKeyRenameDto.cs new file mode 100644 index 0000000000..1455840ecb --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/CryptoKeyRenameDto.cs @@ -0,0 +1,51 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A cryptographic key. + /// + public class CryptoKeyRenameDto + { + /// + /// The key's local name. + /// + public string Was { set; get; } + + /// + /// The key's global unique ID. + /// + public string Now { set; get; } + + /// + /// The key's global unique ID. + /// + public string Id { set; get; } + + /// + /// Indicates that a existing key was overwritten. + /// + public bool Overwrite { set; get; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/CryptoKeysDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/CryptoKeysDto.cs new file mode 100644 index 0000000000..a29e71c319 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/CryptoKeysDto.cs @@ -0,0 +1,38 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A list of cryptographic keys. + /// + public sealed class CryptoKeysDto + { + /// + /// A list of cryptographic keys. + /// + public IEnumerable Keys { set; get; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/DhtPeerDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/DhtPeerDto.cs new file mode 100644 index 0000000000..b2832cbe82 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/DhtPeerDto.cs @@ -0,0 +1,53 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Information from the Distributed Hash Table. + /// + public sealed class DhtPeerDto + { + /// + /// The ID of the peer that provided the response. + /// + internal string Id; + + /// + /// Unknown. + /// + public int Type; // TODO: what is the type? + + /// + /// The peer that has the information. + /// + internal IEnumerable Responses; + + /// + /// Unknown. + /// + public string Extra = string.Empty; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/DhtPeerResponseDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/DhtPeerResponseDto.cs new file mode 100644 index 0000000000..bde03812b9 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/DhtPeerResponseDto.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Information on a peer that has the information. + /// + internal sealed class DhtPeerResponseDto + { + /// + /// The peer ID. + /// + public string Id; + + /// + /// The listening addresses of the peer. + /// + public IEnumerable Addrs; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemDetailDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemDetailDto.cs new file mode 100644 index 0000000000..8094799a44 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemDetailDto.cs @@ -0,0 +1,51 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Details on a files. + /// + internal sealed class FileSystemDetailDto + { + /// + /// The CID of the file. + /// + public string Hash { set; get; } + + /// + /// The file size. + /// + public long Size { set; get; } + + /// + /// "File" or "Directory" + /// + public string Type { set; get; } + + /// + /// Links to other files. + /// + public FileSystemLinkDto[] Links { set; get; } + } +} diff --git a/src/Catalyst.Core.Lib/Repository/MempoolEfCoreRepository.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemDetailsDto.cs similarity index 59% rename from src/Catalyst.Core.Lib/Repository/MempoolEfCoreRepository.cs rename to src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemDetailsDto.cs index 51694e93b1..5ce16c1e4a 100644 --- a/src/Catalyst.Core.Lib/Repository/MempoolEfCoreRepository.cs +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemDetailsDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,18 +21,23 @@ #endregion -using Catalyst.Core.Lib.DAO; -using SharpRepository.EfCoreRepository; -using SharpRepository.Repository.Caching; +using System.Collections.Generic; -namespace Catalyst.Core.Lib.Repository +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto { - public class MempoolEfCoreRepository : EfCoreRepository + /// + /// A map of files. + /// + public sealed class FileSystemDetailsDto { - public MempoolEfCoreRepository(IDbContext dbContext, - ICachingStrategy cachingStrategy = null) : - base((Microsoft.EntityFrameworkCore.DbContext) dbContext, cachingStrategy) { } + /// + /// A path and its CID. + /// + internal Dictionary Arguments { set; get; } + + /// + /// The pins. + /// + internal Dictionary Objects { set; get; } } } - - diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemLinkDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemLinkDto.cs new file mode 100644 index 0000000000..ee74798175 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemLinkDto.cs @@ -0,0 +1,46 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A link to a file. + /// + internal sealed class FileSystemLinkDto + { + /// + /// The file name. + /// + public string Name { set; get; } + + /// + /// The CID of the file. + /// + public string Hash { set; get; } + + /// + /// The file size. + /// + public long Size { set; get; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemNodeDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemNodeDto.cs new file mode 100644 index 0000000000..9f994c9c02 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FileSystemNodeDto.cs @@ -0,0 +1,46 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A created file. + /// + public sealed class FileSystemNodeDto + { + /// + /// The file name. + /// + public string Name { set; get; } + + /// + /// The CID of the file. + /// + public string Hash { set; get; } + + /// + /// The file size. + /// + public string Size { set; get; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FiltersDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FiltersDto.cs new file mode 100644 index 0000000000..18afa42652 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/FiltersDto.cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A list of filters. + /// + public class FiltersDto + { + /// + /// A list of multiaddresses. + /// + public string[] Strings; + } +} diff --git a/src/Catalyst.Core.Lib/Mempool/Documents/MempoolDocument.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/HashDto.cs similarity index 58% rename from src/Catalyst.Core.Lib/Mempool/Documents/MempoolDocument.cs rename to src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/HashDto.cs index 865df23d09..a024015a01 100644 --- a/src/Catalyst.Core.Lib/Mempool/Documents/MempoolDocument.cs +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/HashDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,19 +21,25 @@ #endregion -using Catalyst.Abstractions.Mempool.Documents; -using Catalyst.Core.Lib.Mempool.Models; -using MongoDB.Bson.Serialization.Attributes; using Newtonsoft.Json; -using SharpRepository.Repository; -namespace Catalyst.Core.Lib.Mempool.Documents +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto { - public sealed class MempoolDocument : MempoolItem, IMempoolDocument + /// + /// A hash to some data. + /// + public sealed class HashDto { - [RepositoryPrimaryKey(Order = 1)] - [JsonProperty("id")] - [BsonId] - public string DocumentId => Transaction?.Signature?.RawBytes.ToBase64(); + /// + /// Typically a CID. + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Hash { set; get; } + + /// + /// An error message. + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Error { set; get; } } } diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/KeyDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/KeyDto.cs new file mode 100644 index 0000000000..e8031d1d3b --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/KeyDto.cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A key to some data. + /// + public sealed class KeyDto + { + /// + /// Typically a CID. + /// + internal string Key; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/LinkedDataCidDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/LinkedDataCidDto.cs new file mode 100644 index 0000000000..89f86f09df --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/LinkedDataCidDto.cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A CID as linked data. + /// + public class LinkedDataCidDto + { + /// + /// A link to the CID. + /// + public LinkedDataDto Cid { set; get; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/LinkedDataDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/LinkedDataDto.cs new file mode 100644 index 0000000000..6edc50182e --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/LinkedDataDto.cs @@ -0,0 +1,39 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Newtonsoft.Json; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A link to a CID. + /// + public class LinkedDataDto + { + /// + /// The CID. + /// + [JsonProperty(PropertyName = "/")] + public string Link { set; get; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/MessageDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/MessageDto.cs new file mode 100644 index 0000000000..9d33accf24 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/MessageDto.cs @@ -0,0 +1,70 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using Lib.P2P.PubSub; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A published message. + /// + internal sealed class MessageDto + { + /// + /// The base-64 encoding of the author's peer id. + /// + public string From; + + /// + /// The base-64 encoding of the author's unique sequence number. + /// + public string Seqno; + + /// + /// The base-64 encoding of the message data. + /// + public string Data; + + /// + /// The topics associated with the message. + /// + public string[] TopicIDs; + + /// + /// Create a new instance of the + /// from the . + /// + /// + /// A pubsub messagee. + /// + public MessageDto(IPublishedMessage msg) + { + From = Convert.ToBase64String(msg.Sender.Id.ToArray()); + Seqno = Convert.ToBase64String(msg.SequenceNumber); + Data = Convert.ToBase64String(msg.DataBytes); + TopicIDs = msg.Topics.ToArray(); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/NamedContentDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/NamedContentDto.cs new file mode 100644 index 0000000000..f35d4e793c --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/NamedContentDto.cs @@ -0,0 +1,41 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Content that has an associated name. + /// + public sealed class NamedContentDto + { + /// + /// Path to the name, "/ipns/...". + /// + internal string Name { set; get; } + + /// + /// Path to the content, "/ipfs/...". + /// + internal string Value { set; get; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PathDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PathDto.cs new file mode 100644 index 0000000000..0a700df47d --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PathDto.cs @@ -0,0 +1,42 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A path to some data. + /// + public sealed class PathDto + { + /// + /// Something like "/ipfs/QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao". + /// + private string Path; + + /// + /// Create a new path. + /// + /// + internal PathDto(string path) { Path = path; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PeerDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PeerDto.cs new file mode 100644 index 0000000000..bffc4fb18a --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PeerDto.cs @@ -0,0 +1,72 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Linq; +using Lib.P2P; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Information on a peer. + /// + public class PeerInfoDto + { + /// + /// The unique ID of the peer. + /// + public string Id; + + /// + /// The public key of the peer. + /// + public string PublicKey; + + /// + /// The addresses that the peer is listening on. + /// + public IEnumerable Addresses; + + /// + /// The version of the software. + /// + public string AgentVersion; + + /// + /// The version of the protocol. + /// + public string ProtocolVersion; + + /// + /// Creates a new peer info. + /// + public PeerInfoDto(Peer peer) + { + Id = peer.Id.ToBase58(); + PublicKey = peer.PublicKey; + Addresses = peer.Addresses.Select(a => a.ToString()); + AgentVersion = peer.AgentVersion; + ProtocolVersion = peer.ProtocolVersion; + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PinDetailDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PinDetailDto.cs new file mode 100644 index 0000000000..871c500039 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PinDetailDto.cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Detailed information on a pin. + /// + public sealed class PinDetailDto + { + /// + /// "recursive", "indirect", ... + /// + public string Type = "unknown"; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PinDetailsDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PinDetailsDto.cs new file mode 100644 index 0000000000..561dd71ada --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PinDetailsDto.cs @@ -0,0 +1,38 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A map of pins. + /// + public class PinDetailsDto + { + /// + /// The pins. + /// + public Dictionary Keys; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PinsDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PinsDto.cs new file mode 100644 index 0000000000..b05728470a --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PinsDto.cs @@ -0,0 +1,38 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A list of pins. + /// + public sealed class PinsDto + { + /// + /// The CIDs. + /// + public IEnumerable Pins; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PubSubPeersDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PubSubPeersDto.cs new file mode 100644 index 0000000000..c0e8975240 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PubSubPeersDto.cs @@ -0,0 +1,38 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A list of peers. + /// + public class PubSubPeersDto + { + /// + /// A list of peer IDs. + /// + public IEnumerable Strings; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PubSubTopicsDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PubSubTopicsDto.cs new file mode 100644 index 0000000000..55c4d2a0a2 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/PubSubTopicsDto.cs @@ -0,0 +1,38 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A list of topics. + /// + public class PubSubTopicsDto + { + /// + /// A list of topics. + /// + public IEnumerable Strings; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/StatsBitSwapDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/StatsBitSwapDto.cs new file mode 100644 index 0000000000..c1f842464c --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/StatsBitSwapDto.cs @@ -0,0 +1,86 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// Statistics for bitswap. + /// + public class StatsBitSwapDto + { + /// + /// TODO: Unknown. + /// + public int ProvideBufLen { set; get; } + + /// + /// The content IDs that are wanted. + /// + public IEnumerable Wantlist { set; get; } + + /// + /// The known peers. + /// + public IEnumerable Peers { set; get; } + + /// + /// The number of blocks sent by other peers. + /// + public ulong BlocksReceived { set; get; } + + /// + /// The number of bytes sent by other peers. + /// + public ulong DataReceived { set; get; } + + /// + /// The number of blocks sent to other peers. + /// + public ulong BlocksSent { set; get; } + + /// + /// The number of bytes sent to other peers. + /// + public ulong DataSent { set; get; } + + /// + /// The number of duplicate blocks sent by other peers. + /// + /// + /// A duplicate block is a block that is already stored in the + /// local repository. + /// + public ulong DupBlksReceived { set; get; } + + /// + /// The number of duplicate bytes sent by other peers. + /// + /// + /// A duplicate block is a block that is already stored in the + /// local repository. + /// + public ulong DupDataReceived { set; get; } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/VersionBlockRepositoryDto.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/VersionBlockRepositoryDto.cs new file mode 100644 index 0000000000..2ebd243c96 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Dto/VersionBlockRepositoryDto.cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Dto +{ + /// + /// A wrapped version number. + /// + public sealed class VersionBlockRepositoryDto + { + /// + /// The version number. + /// + internal string Version; + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Filter/ApiExceptionFilter.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Filter/ApiExceptionFilter.cs new file mode 100644 index 0000000000..ff3222e4d7 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Filter/ApiExceptionFilter.cs @@ -0,0 +1,90 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Reflection; +using System.Threading.Tasks; +using Catalyst.Core.Modules.Dfs.WebApi.V0.Response; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Filter +{ + /// + /// Handles exceptions thrown by a controller. + /// + /// + /// Returns a to the caller. + /// + internal sealed class ApiExceptionFilter : ExceptionFilterAttribute + { + /// + public override void OnException(ExceptionContext context) + { + var statusCode = 500; // Internal Server Error + var message = context.Exception.Message; + string[] details = null; + + switch (context.Exception) + { + // Map special exceptions to a status code. + case FormatException _: + // Bad Request + case KeyNotFoundException _: + statusCode = 400; // Bad Request + break; + case TaskCanceledException _: + statusCode = 504; // Gateway Timeout + message = "The request took too long to process or was cancelled."; + break; + case NotImplementedException _: + statusCode = 501; // Not Implemented + break; + case TargetInvocationException _: + message = context.Exception.InnerException?.Message; + break; + } + + // Internal Server Error or Not Implemented get a stack dump. + if (statusCode == 500 || statusCode == 501) + { + details = context.Exception.StackTrace.Split(Environment.NewLine); + } + + context.HttpContext.Response.StatusCode = statusCode; + context.Result = new JsonResult(new ApiError + { + Message = message, + Details = details + }); + + // Remove any caching headers + context.HttpContext.Response.Headers.Remove("cache-control"); + context.HttpContext.Response.Headers.Remove("etag"); + context.HttpContext.Response.Headers.Remove("last-modified"); + + base.OnException(context); + } + } +} diff --git a/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Response/ApiError.cs b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Response/ApiError.cs new file mode 100644 index 0000000000..abca00f7b0 --- /dev/null +++ b/src/Catalyst.Core.Modules.Dfs/WebApi/V0/Response/ApiError.cs @@ -0,0 +1,50 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Newtonsoft.Json; + +namespace Catalyst.Core.Modules.Dfs.WebApi.V0.Response +{ + /// + /// The standard error response for failing API calls. + /// + public sealed class ApiError + { + /// + /// Human readable description of the error. + /// + public string Message { get; set; } + + /// + /// Developer readable description of the error. + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string[] Details { get; set; } + + /// + /// A standard ??? error code. + /// + [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] + public string Code { get; set; } + } +} diff --git a/src/Catalyst.Core.Modules.Hashing.Tests/Catalyst.Core.Modules.Hashing.Tests.csproj b/src/Catalyst.Core.Modules.Hashing.Tests/Catalyst.Core.Modules.Hashing.Tests.csproj index f7889d1879..0e5d059367 100644 --- a/src/Catalyst.Core.Modules.Hashing.Tests/Catalyst.Core.Modules.Hashing.Tests.csproj +++ b/src/Catalyst.Core.Modules.Hashing.Tests/Catalyst.Core.Modules.Hashing.Tests.csproj @@ -1,19 +1,24 @@ - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Consensus.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Hashing.Tests.snk true + + 1701;1702;VSTHRD200;CS8002 + - - - - - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + diff --git a/src/Catalyst.Core.Modules.Hashing.Tests/UnitTests/HashingTests.cs b/src/Catalyst.Core.Modules.Hashing.Tests/UnitTests/HashingTests.cs index 6649ecb342..dd60c0ade4 100644 --- a/src/Catalyst.Core.Modules.Hashing.Tests/UnitTests/HashingTests.cs +++ b/src/Catalyst.Core.Modules.Hashing.Tests/UnitTests/HashingTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,12 +22,15 @@ #endregion using System; +using System.Linq; using Autofac; using Catalyst.Abstractions.Hashing; using Catalyst.Core.Modules.Hashing; +using Catalyst.Protocol.Transaction; using FluentAssertions; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using MultiFormats.Registry; +using Google.Protobuf; +using NUnit.Framework; namespace Catalyst.Core.Modules.Consensus.Tests.UnitTests { @@ -44,16 +47,16 @@ public HashingTests() _container.BeginLifetimeScope(); } - [Fact] + [Test] public void HashProvider_Can_Be_Resolved() { _container.Resolve().Should().NotBeNull(); } - [Fact] + [Test] public void MultihashAlgorithm_Can_Be_Resolved() { _container.Resolve().Should().NotBeNull(); } - [Fact] + [Test] public void Can_Hash_Data() { var hashProvider = _container.Resolve(); @@ -61,5 +64,31 @@ public void Can_Hash_Data() var multiHash = hashProvider.ComputeMultiHash(data); multiHash.Should().NotBeNull(); } + + [Test] + public void Hashes_messages() + { + var hashProvider = _container.Resolve(); + var entry = new PublicEntry(); + + var arrayHash = hashProvider.ComputeMultiHash(entry.ToByteArray()); + var messageHash = hashProvider.ComputeMultiHash(entry); + + arrayHash.ToArray().Should().BeEquivalentTo(messageHash.ToArray()); + } + + [Test] + public void Hashes_messages_with_suffix() + { + var hashProvider = _container.Resolve(); + var entry = new PublicEntry(); + + var suffix = new byte[1]; + + var arrayHash = hashProvider.ComputeMultiHash(entry.ToByteArray().Concat(suffix).ToArray()); + var messageHash = hashProvider.ComputeMultiHash(entry, suffix); + + arrayHash.ToArray().Should().BeEquivalentTo(messageHash.ToArray()); + } } } diff --git a/src/Catalyst.Core.Modules.Hashing/Catalyst.Core.Modules.Hashing.csproj b/src/Catalyst.Core.Modules.Hashing/Catalyst.Core.Modules.Hashing.csproj index 83b555715b..e47d7e28be 100644 --- a/src/Catalyst.Core.Modules.Hashing/Catalyst.Core.Modules.Hashing.csproj +++ b/src/Catalyst.Core.Modules.Hashing/Catalyst.Core.Modules.Hashing.csproj @@ -1,18 +1,22 @@ - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Hashing - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Hashing.snk true + + 1701;1702;CS8002 + - + + diff --git a/src/Catalyst.Core.Modules.Hashing/Exception/MultiHashNotValidException.cs b/src/Catalyst.Core.Modules.Hashing/Exception/MultiHashNotValidException.cs index 8495eb63e1..434be3618f 100644 --- a/src/Catalyst.Core.Modules.Hashing/Exception/MultiHashNotValidException.cs +++ b/src/Catalyst.Core.Modules.Hashing/Exception/MultiHashNotValidException.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Hashing/HashProvider.cs b/src/Catalyst.Core.Modules.Hashing/HashProvider.cs index 8babb53891..1709f80193 100644 --- a/src/Catalyst.Core.Modules.Hashing/HashProvider.cs +++ b/src/Catalyst.Core.Modules.Hashing/HashProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,14 +26,44 @@ using System.Linq; using System.Text; using Catalyst.Abstractions.Hashing; -using TheDotNetLeague.MultiFormats.MultiHash; +using MultiFormats; +using MultiFormats.Registry; +using Google.Protobuf; namespace Catalyst.Core.Modules.Hashing { - public class HashProvider : IHashProvider + public sealed class HashProvider : IHashProvider { public HashingAlgorithm HashingAlgorithm { set; get; } + /// + /// Parse a string representation of a multihash + /// + /// + /// + public static MultiHash Parse(string buffer) { return new MultiHash(buffer); } + + /// + /// Parse a byte array representation of a multihash + /// + /// + /// + public static MultiHash Parse(byte[] buffer) { return new MultiHash(buffer); } + + /// + /// Parse a memory stream representation of a multihash + /// + /// + /// + public static MultiHash Parse(MemoryStream stream) { return new MultiHash(stream); } + + /// + /// Parse a protobuff coded input stream representation of a multihash + /// + /// + /// + public static MultiHash Parse(CodedInputStream stream) { return new MultiHash(stream); } + public HashProvider(HashingAlgorithm hashingAlgorithm) { HashingAlgorithm = hashingAlgorithm; } public MultiHash Cast(byte[] data) { return CastIfHashIsValid(data); } @@ -65,5 +95,13 @@ private MultiHash CastIfHashIsValid(byte[] data) public MultiHash ComputeMultiHash(byte[] data) { return MultiHash.ComputeHash(data, HashingAlgorithm.Name); } public MultiHash ComputeMultiHash(Stream data) { return MultiHash.ComputeHash(data, HashingAlgorithm.Name); } + + public MultiHash ComputeMultiHash(byte[] data, int offset, int count) + { + using (var algorithm = HashingAlgorithm.Hasher()) + { + return new MultiHash(HashingAlgorithm.Name, algorithm.ComputeHash(data, offset, count)); + } + } } } diff --git a/src/Catalyst.Core.Modules.Hashing/HashingModule.cs b/src/Catalyst.Core.Modules.Hashing/HashingModule.cs index fe913811ee..69be61afa7 100644 --- a/src/Catalyst.Core.Modules.Hashing/HashingModule.cs +++ b/src/Catalyst.Core.Modules.Hashing/HashingModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,15 +23,15 @@ using Autofac; using Catalyst.Abstractions.Hashing; -using TheDotNetLeague.MultiFormats.MultiHash; +using MultiFormats.Registry; namespace Catalyst.Core.Modules.Hashing { - public class HashingModule : Module + public sealed class HashingModule : Module { protected override void Load(ContainerBuilder builder) { - var hashingAlgorithm = HashingAlgorithm.GetAlgorithmMetadata("blake2b-256"); + var hashingAlgorithm = HashingAlgorithm.GetAlgorithmMetadata("keccak-256"); builder.RegisterInstance(hashingAlgorithm).SingleInstance(); builder.RegisterType().As().SingleInstance(); } diff --git a/src/Catalyst.Core.Modules.KeySigner.Tests/Catalyst.Core.Modules.KeySigner.Tests.csproj b/src/Catalyst.Core.Modules.KeySigner.Tests/Catalyst.Core.Modules.KeySigner.Tests.csproj index d58950caf4..731a3dd65a 100644 --- a/src/Catalyst.Core.Modules.KeySigner.Tests/Catalyst.Core.Modules.KeySigner.Tests.csproj +++ b/src/Catalyst.Core.Modules.KeySigner.Tests/Catalyst.Core.Modules.KeySigner.Tests.csproj @@ -1,15 +1,25 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.KeySigner.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.KeySigner.Tests.snk true - + + 1701;1702;VSTHRD200;CS8002 + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/Catalyst.Core.Modules.KeySigner.Tests/IntegrationTests/KeySignerIntegrationTests.cs b/src/Catalyst.Core.Modules.KeySigner.Tests/IntegrationTests/KeySignerIntegrationTests.cs index 7cf83c3ccd..d8bfcf3c33 100644 --- a/src/Catalyst.Core.Modules.KeySigner.Tests/IntegrationTests/KeySignerIntegrationTests.cs +++ b/src/Catalyst.Core.Modules.KeySigner.Tests/IntegrationTests/KeySignerIntegrationTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -35,38 +35,44 @@ using Catalyst.Protocol.Network; using Catalyst.TestUtils; using FluentAssertions; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.FileSystem; namespace Catalyst.Core.Modules.KeySigner.Tests.IntegrationTests { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class KeySignerIntegrationTests : FileSystemBasedTest { - public KeySignerIntegrationTests(ITestOutputHelper output) : base(output) + [SetUp] + public void Init() { - var logger = Substitute.For(); + this.Setup(TestContext.CurrentContext); - var passwordManager = Substitute.For(); - - var cryptoContext = new FfiWrapper(); + FfiWrapper cryptoContext = new(); var peerSettings = Substitute.For(); peerSettings.NetworkType.Returns(NetworkType.Devnet); - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + KeyRegistry keyRegistry = new(); - var keystore = new LocalKeyStore(passwordManager, cryptoContext, FileSystem, hashProvider, - logger); + InMemoryStore inMemoryStore = new(); + KeyStoreService keyStoreService = new(new KeyChainOptions { DefaultKeyType = "ed25519" }, inMemoryStore); + KeyApi keyApi = new(keyStoreService); - var keyRegistry = new KeyRegistry(); + var secureString = TestPasswordReader.BuildSecureStringPassword("password"); + keyApi.SetPassphraseAsync(secureString).GetAwaiter().GetResult(); + keyApi.CreateAsync("self", "ed25519", 0).GetAwaiter().GetResult(); - _keySigner = new KeySigner(keystore, cryptoContext, keyRegistry); + _keySigner = new KeySigner(cryptoContext, keyApi, keyRegistry); } - private readonly IKeySigner _keySigner; + private IKeySigner _keySigner; private void Ensure_A_KeyStore_File_Exists() { @@ -80,23 +86,20 @@ private void Ensure_A_KeyStore_File_Exists() Constants.KeyStoreDataSubDir, json); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void KeySigner_Can_Sign_If_There_Is_No_Keystore_File() { _keySigner.Sign(Encoding.UTF8.GetBytes("sign this plz"), new SigningContext()); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void KeySigner_Can_Sign_If_There_Is_An_Existing_Keystore_File() { Ensure_A_KeyStore_File_Exists(); _keySigner.Sign(Encoding.UTF8.GetBytes("sign this plz"), new SigningContext()); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void KeySigner_Can_Verify_A_Signature() { var toSign = Encoding.UTF8.GetBytes("sign this plz"); @@ -105,8 +108,7 @@ public void KeySigner_Can_Verify_A_Signature() _keySigner.Verify(signature, toSign, new SigningContext()).Should().BeTrue(); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void KeySigner_Cant_Verify_An_Incorrect_Signature() { var toSign = Encoding.UTF8.GetBytes("sign this plz"); @@ -120,8 +122,7 @@ public void KeySigner_Cant_Verify_An_Incorrect_Signature() _keySigner.Verify(signature, toSign, signingContext).Should().BeFalse(); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void KeySigner_Can_Verify_A_Signature_With_Non_Default_Context() { var toSign = Encoding.UTF8.GetBytes("sign this plz"); diff --git a/src/Catalyst.Core.Modules.KeySigner.Tests/UnitTests/KeySignerTests.cs b/src/Catalyst.Core.Modules.KeySigner.Tests/UnitTests/KeySignerTests.cs index d13f042348..3b39d41290 100644 --- a/src/Catalyst.Core.Modules.KeySigner.Tests/UnitTests/KeySignerTests.cs +++ b/src/Catalyst.Core.Modules.KeySigner.Tests/UnitTests/KeySignerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,108 +25,86 @@ using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.Keystore; using Catalyst.Abstractions.Types; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Cryptography.BulletProofs; +using Catalyst.Protocol; using Catalyst.Protocol.Cryptography; -using Catalyst.Protocol.Network; using FluentAssertions; using NSubstitute; -using Xunit; +using NUnit.Framework; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; namespace Catalyst.Core.Modules.KeySigner.Tests.UnitTests { public sealed class KeySignerTests { - private readonly IKeyStore _keystore; - private readonly IKeyRegistry _keyRegistry; - private readonly ISignature _signature; - private readonly IPrivateKey _privateKey; - private readonly SigningContext _signingContext; + private IKeyApi _keyApi; + private IKeyRegistry _keyRegistry; + private IPrivateKey _privateKey; + private SigningContext _signingContext; private ICryptoContext _cryptoContext; - public KeySignerTests() + [SetUp] + public void Init() { - _keystore = Substitute.For(); + _keyApi = Substitute.For(); _keyRegistry = Substitute.For(); - _signature = Substitute.For(); _privateKey = Substitute.For(); - _cryptoContext = Substitute.For(); + _cryptoContext = new FfiWrapper(); - _cryptoContext.Sign(default, default, default).ReturnsForAnyArgs(_signature); - _privateKey.Bytes.Returns(ByteUtil.GenerateRandomByteArray(32)); + var g = GeneratorUtilities.GetKeyPairGenerator("Ed25519"); + SecureRandom random = new(); + g.Init(new Ed25519KeyGenerationParameters(random)); + var keyPair = g.GenerateKeyPair(); - _keystore.KeyStoreDecrypt(default).ReturnsForAnyArgs(_privateKey); - - _signingContext = new SigningContext(); - } - - [Fact] - public void On_Init_KeySigner_Can_Retrieve_Key_From_KeyStore_If_Key_Doesnt_Initially_Exist_In_Registry() - { - _keyRegistry.GetItemFromRegistry(default).ReturnsForAnyArgs((IPrivateKey) null); - _keyRegistry.RegistryContainsKey(default).ReturnsForAnyArgs(false); - _keyRegistry.AddItemToRegistry(default, default).ReturnsForAnyArgs(true); - - var keySigner = new KeySigner(_keystore, _cryptoContext, _keyRegistry); - _keystore.Received(1).KeyStoreDecrypt(Arg.Any()); - _keyRegistry.ReceivedWithAnyArgs(1).AddItemToRegistry(default, default); - keySigner.Should().NotBe(null); - } - - [Fact] - public void On_Init_KeySigner_Can_Generate_Default_Key_If_Key_No_KeyStore_File_Exists() - { - _keyRegistry.GetItemFromRegistry(default).ReturnsForAnyArgs((IPrivateKey) null); - _keyRegistry.RegistryContainsKey(default).ReturnsForAnyArgs(false); - _keyRegistry.AddItemToRegistry(default, default).ReturnsForAnyArgs(true); - - _keystore.KeyStoreDecrypt(default).ReturnsForAnyArgs((IPrivateKey) null); + _keyApi.GetPrivateKeyAsync(KeyRegistryTypes.DefaultKey.Name).Returns(keyPair.Private); + _privateKey.Bytes.Returns(((Ed25519PrivateKeyParameters) keyPair.Private).GetEncoded()); - var keySigner = new KeySigner(_keystore, _cryptoContext, _keyRegistry); - _keystore.Received(1).KeyStoreDecrypt(Arg.Any()); - _keystore.Received(1)?.KeyStoreGenerateAsync(Arg.Any(), Arg.Any()); - _keyRegistry.ReceivedWithAnyArgs(1).AddItemToRegistry(default, default); - keySigner.Should().NotBe(null); + _signingContext = new SigningContext(); } - [Fact] + [Test] public void KeySigner_Can_Sign_If_Key_Exists_In_Registry() { _keyRegistry.GetItemFromRegistry(default).ReturnsForAnyArgs(_privateKey); _keyRegistry.RegistryContainsKey(default).ReturnsForAnyArgs(true); _keyRegistry.AddItemToRegistry(default, default).ReturnsForAnyArgs(true); - var keySigner = new KeySigner(_keystore, _cryptoContext, _keyRegistry); + KeySigner keySigner = new(_cryptoContext, Substitute.For(), _keyRegistry); _keyRegistry.ReceivedWithAnyArgs(0).AddItemToRegistry(default, default); - _keystore.ClearReceivedCalls(); _keyRegistry.ClearReceivedCalls(); - var actualSignature = keySigner.Sign(Encoding.UTF8.GetBytes("sign this please"), _signingContext); + var content = Encoding.UTF8.GetBytes("sign this please"); + var actualSignature = keySigner.Sign(content, _signingContext); _keyRegistry.ReceivedWithAnyArgs(1).GetItemFromRegistry(default); - _keystore.Received(0).KeyStoreDecrypt(Arg.Any()); - _keystore.Received(0)?.KeyStoreGenerateAsync(Arg.Any(), Arg.Any()); _keyRegistry.ReceivedWithAnyArgs(0).AddItemToRegistry(default, default); - - Assert.Equal(_signature, actualSignature); + + using var pooled = _signingContext.SerializeToPooledBytes(); + var signature = _cryptoContext.Sign(_privateKey, content, pooled.Span); + signature.PublicKeyBytes.Should().BeEquivalentTo(actualSignature.PublicKeyBytes); + signature.SignatureBytes.Should().BeEquivalentTo(actualSignature.SignatureBytes); } - [Fact] + [Test] public void KeySigner_Can_Sign_If_Key_Doesnt_Exists_In_Registry_But_There_Is_A_Keystore_File() - { - var keySigner = new KeySigner(_keystore, _cryptoContext, _keyRegistry); - _keystore.ClearReceivedCalls(); + { + KeySigner keySigner = new(_cryptoContext, _keyApi, _keyRegistry); _keyRegistry.ClearReceivedCalls(); - _keyRegistry.GetItemFromRegistry(default).ReturnsForAnyArgs(null, _privateKey); - _keyRegistry.RegistryContainsKey(default).ReturnsForAnyArgs(true); + _keyRegistry.RegistryContainsKey(default).ReturnsForAnyArgs(false); _keyRegistry.AddItemToRegistry(default, default).ReturnsForAnyArgs(true); - - var actualSignature = keySigner.Sign(Encoding.UTF8.GetBytes("sign this please"), _signingContext); - _keystore.Received(1).KeyStoreDecrypt(Arg.Any()); + var content = Encoding.UTF8.GetBytes("sign this please"); + var actualSignature = keySigner.Sign(content, _signingContext); + + _keyApi.Received(1).GetPrivateKeyAsync(KeyRegistryTypes.DefaultKey.Name); - Assert.Equal(_signature, actualSignature); + using var pooled = _signingContext.SerializeToPooledBytes(); + var signature = _cryptoContext.Sign(_privateKey, content, pooled.Span); + signature.PublicKeyBytes.Should().BeEquivalentTo(actualSignature.PublicKeyBytes); + signature.SignatureBytes.Should().BeEquivalentTo(actualSignature.SignatureBytes); } } } diff --git a/src/Catalyst.Core.Modules.KeySigner/Catalyst.Core.Modules.KeySigner.csproj b/src/Catalyst.Core.Modules.KeySigner/Catalyst.Core.Modules.KeySigner.csproj index c67dd97b04..384bb43cff 100644 --- a/src/Catalyst.Core.Modules.KeySigner/Catalyst.Core.Modules.KeySigner.csproj +++ b/src/Catalyst.Core.Modules.KeySigner/Catalyst.Core.Modules.KeySigner.csproj @@ -1,12 +1,15 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.KeySigner - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.KeySigner.snk true + + 1701;1702;CS8002 + diff --git a/src/Catalyst.Core.Modules.KeySigner/KeySigner.cs b/src/Catalyst.Core.Modules.KeySigner/KeySigner.cs index 9f36fbdccd..e04190188f 100644 --- a/src/Catalyst.Core.Modules.KeySigner/KeySigner.cs +++ b/src/Catalyst.Core.Modules.KeySigner/KeySigner.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,104 +22,70 @@ #endregion using System; -using System.Threading.Tasks; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.Keystore; using Catalyst.Abstractions.Types; +using Catalyst.Protocol; using Catalyst.Protocol.Cryptography; -using Catalyst.Protocol.Network; -using Google.Protobuf; -using Org.BouncyCastle.Security; +using Org.BouncyCastle.Crypto.Parameters; namespace Catalyst.Core.Modules.KeySigner { public sealed class KeySigner : IKeySigner { - private readonly IKeyStore _keyStore; private readonly ICryptoContext _cryptoContext; + private readonly IKeyApi _keyApi; private readonly IKeyRegistry _keyRegistry; - private readonly KeyRegistryTypes _defaultKey = KeyRegistryTypes.DefaultKey; /// Initializes a new instance of the class. /// The key store. /// The crypto context. /// /// The key registry. - public KeySigner(IKeyStore keyStore, - ICryptoContext cryptoContext, + public KeySigner(ICryptoContext cryptoContext, + IKeyApi keyApi, IKeyRegistry keyRegistry) { - _keyStore = keyStore; _cryptoContext = cryptoContext; + _keyApi = keyApi; _keyRegistry = keyRegistry; - InitialiseKeyRegistry(); - } - - private void InitialiseKeyRegistry() - { - if (!TryPopulateDefaultKeyFromKeyStore(out _)) - { - GenerateKeyAndPopulateRegistryWithDefaultAsync().ConfigureAwait(false); - } - } - - private async Task GenerateKeyAndPopulateRegistryWithDefaultAsync() - { - var privateKey = await _keyStore.KeyStoreGenerateAsync(NetworkType.Devnet, _defaultKey).ConfigureAwait(false); - if (privateKey != null) - { - _keyRegistry.AddItemToRegistry(_defaultKey, privateKey); - } } - /// - IKeyStore IKeySigner.KeyStore => _keyStore; - /// ICryptoContext IKeySigner.CryptoContext => _cryptoContext; - private ISignature Sign(byte[] data, SigningContext signingContext, KeyRegistryTypes keyIdentifier) + public IPrivateKey GetPrivateKey(KeyRegistryTypes keyIdentifier) { - var privateKey = _keyRegistry.GetItemFromRegistry(keyIdentifier); - if (privateKey == null && !TryPopulateRegistryFromKeyStore(keyIdentifier, out privateKey)) + if (_keyRegistry.RegistryContainsKey(keyIdentifier)) { - throw new SignatureException("The signature cannot be created because the key does not exist"); + return _keyRegistry.GetItemFromRegistry(keyIdentifier); } - return Sign(data, signingContext, privateKey); - } + var privateKeyParameters = (Ed25519PrivateKeyParameters) _keyApi.GetPrivateKeyAsync(keyIdentifier.Name).GetAwaiter().GetResult(); + var privateKeyBytes = privateKeyParameters.GetEncoded(); + var privateKey = _cryptoContext.GetPrivateKeyFromBytes(privateKeyBytes); + _keyRegistry.AddItemToRegistry(keyIdentifier, privateKey); - public ISignature Sign(byte[] data, SigningContext signingContext) - { - return Sign(data, signingContext, KeyRegistryTypes.DefaultKey); + return privateKey; } - private ISignature Sign(byte[] data, SigningContext signingContext, IPrivateKey privateKey) + public ISignature Sign(ReadOnlySpan data, SigningContext signingContext) { - return _cryptoContext.Sign(privateKey, data, signingContext.ToByteArray()); - } + var privateKey = GetPrivateKey(KeyRegistryTypes.DefaultKey); - /// - public bool Verify(ISignature signature, byte[] message, SigningContext signingContext) - { - return _cryptoContext.Verify(signature, message, signingContext.ToByteArray()); - } + using var pooled = signingContext.SerializeToPooledBytes(); - public void ExportKey() - { - throw new NotImplementedException(); - } + var signature = _cryptoContext.Sign(privateKey, data, pooled.Span); - private bool TryPopulateRegistryFromKeyStore(KeyRegistryTypes keyIdentifier, out IPrivateKey key) - { - key = _keyStore.KeyStoreDecrypt(keyIdentifier); - - return key != null && (_keyRegistry.RegistryContainsKey(keyIdentifier) || _keyRegistry.AddItemToRegistry(keyIdentifier, key)); + return signature; } - private bool TryPopulateDefaultKeyFromKeyStore(out IPrivateKey key) + /// + public bool Verify(ISignature signature, ReadOnlySpan data, SigningContext signingContext) { - return TryPopulateRegistryFromKeyStore(_defaultKey, out key); - } + using var pooled = signingContext.SerializeToPooledBytes(); + + return _cryptoContext.Verify(signature, data, pooled.Span); + } } } diff --git a/src/Catalyst.Core.Modules.KeySigner/KeySignerModule.cs b/src/Catalyst.Core.Modules.KeySigner/KeySignerModule.cs index 2d7687e6fb..9a8bf878cd 100644 --- a/src/Catalyst.Core.Modules.KeySigner/KeySignerModule.cs +++ b/src/Catalyst.Core.Modules.KeySigner/KeySignerModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Keystore.Tests/Catalyst.Core.Modules.Keystore.Tests.csproj b/src/Catalyst.Core.Modules.Keystore.Tests/Catalyst.Core.Modules.Keystore.Tests.csproj index 04c988e2f4..5aa40414ad 100644 --- a/src/Catalyst.Core.Modules.Keystore.Tests/Catalyst.Core.Modules.Keystore.Tests.csproj +++ b/src/Catalyst.Core.Modules.Keystore.Tests/Catalyst.Core.Modules.Keystore.Tests.csproj @@ -1,18 +1,29 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Keystore.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Keystore.Tests.snk true - + + 1701;1702;VSTHRD200;CS8002 + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/src/Catalyst.Core.Modules.Keystore.Tests/IntegrationTests/KeyApiTest.cs b/src/Catalyst.Core.Modules.Keystore.Tests/IntegrationTests/KeyApiTest.cs new file mode 100644 index 0000000000..0721990686 --- /dev/null +++ b/src/Catalyst.Core.Modules.Keystore.Tests/IntegrationTests/KeyApiTest.cs @@ -0,0 +1,359 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Security; +using System.Threading.Tasks; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.Config; +using Catalyst.TestUtils; +using Lib.P2P; +using Makaretu.Dns; +using NSubstitute; +using Org.BouncyCastle.Crypto.Parameters; +using NUnit.Framework; + + +namespace Catalyst.Core.Modules.Keystore.Tests.IntegrationTests +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class KeyApiTest : FileSystemBasedTest + { + private IKeyStoreService _keyStoreService; + + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); + + var keyChain = Substitute.For(); + _keyStoreService = new KeyStoreService(keyChain, new KeyFileStore(new RepositoryOptions(FileSystem, Constants.DfsDataSubDir))); + _keyStoreService.SetPassphraseAsync(new SecureString()).Wait(); + } + + [Test] + public async Task Self_Key_Exists() + { + const string name = "self"; + var _ = await _keyStoreService.CreateAsync(name, "ed25519", 0); + + var keys = await _keyStoreService.ListAsync(); + var self = keys.Single(k => k.Name == "self"); + + var me = await _keyStoreService.FindKeyByNameAsync("self"); + Assert.AreEqual("self", self.Name); + Assert.AreEqual(me.Id, self.Id); + } + + [Test] + public async Task Export_Import() + { + const string name = "self"; + var _ = await _keyStoreService.CreateAsync(name, "ed25519", 0); + var password = "password".ToCharArray(); + var pem = await _keyStoreService.ExportAsync(name, password); + Assert.That(pem, Does.StartWith("-----BEGIN ENCRYPTED PRIVATE KEY-----")); + + var keys = await _keyStoreService.ListAsync(); + var self = keys.Single(k => k.Name == name); + + await _keyStoreService.RemoveAsync("clone"); + var clone = await _keyStoreService.ImportAsync("clone", pem, password); + Assert.AreEqual("clone", clone.Name); + Assert.AreEqual(self.Id, clone.Id); + } + + [Test] + public void Export_Unknown_Key() + { + var password = "password".ToCharArray(); + ExceptionAssert.Throws(() => + { + var _ = _keyStoreService.ExportAsync("unknown", password).Result; + }); + } + + [Test] + public async Task Import_Wrong_Password() + { + const string name = "self"; + var _ = await _keyStoreService.CreateAsync(name, "ed25519", 0); + + var password = "password".ToCharArray(); + var pem = await _keyStoreService.ExportAsync("self", password); + + var wrong = "wrong password".ToCharArray(); + ExceptionAssert.Throws(() => + { + var _ = _keyStoreService.ImportAsync("clone", pem, wrong).Result; + }); + } + + [Test] + public async Task Import_JSIPFS_Node() + { + const string pem = @"-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDTA/BgkqhkiG9w0BBQ0wMjAaBgkqhkiG9w0BBQwwDQQILdGJynKmkrMCAWQw +FAYIKoZIhvcNAwcECByaxdAET2tuBIIEyCKPITRayWR57HOJeTooJVR4tFCaNIo+ +ThspwXbk+EqkhQUOcmn+OrgizxL9/sX1l+VlZYR9NkWqbaKo9yeZCX79p64MvUvp +IplgXxEf+rdfZ5xPQKN2Rfv7DyHW5h0JKMEISSLpgtA4Pc0Sr7PQdkLCS0tIF8yL +FEo7YA+yrmsUQbIeVabMLG0DaN2/csydp26IfldAOjqQgy5YIG/xz6gtISfXquxZ +YLPghpM4l/vK2IgxTbKPXQCLJ34rVRvIulIe0Zs+a9Om9O0uLZtTcM0VzlbIH6H7 +fJlx6poxkBIG/Ui3nIjiOHa5DnqAxCNxkRH9TEBjoqFkPYQ1ExvfIIic2JqT8JUO +nKX9vGuudS/MqAEUO8EvrI68F4E+7zc/ahh/S3PQVhMZuR8ajblUZUxnItXgFt/0 +mnOca3HNB2hLz83ubBvr6E9Nt/7AddIfaZkuIXkrXmz7LfelIsslUk4YIy7YchMv +Z1heLasChKVL7GEWoqXBv1erks+l8tpTe/iS/d4sWT8AiTFfPZu53TZ98vGtjLNO +RdaTNYP3tWyWCwQAPchHF9wLHCFjTrC2gsYgqalv2tYhHSqQg5lhDi9u+Z7bMihT +WzXVp7ddkYXX7wgD6yuPQWeKgkIlfKjiHMs6sfc5UBVEJWlgDtTl6DdUa0LaqGWF +J3b6Pc0f1NXNPN+iZlhO50eBunPUd0HT1tbdIwM9sqdDOe8+O55kUcXVeQAqPgj+ +Qo0wi+L6bGSIhGXnoYPNn9al0ANNYV2Y2KJGPiWVEq8eLt3pFlhb3ELCEjgno9Sf +lYyIrP1hy9hfoN2sFM/s2Rn7/9n5u3/Gi9hVi8VlZGa7j4LLquP52ZLYBLOypwc3 +2EDHw47r2TTKIcfiEwAEpuFuZDWyD0AgL0JwKYVKnwgzZIOiAW00Xn9FWmZQBtqK +Pb7jWArvmjvBK7cc0rLfLu2x4pF2DujeDxvru047nv97xiRpCpCLe6o5PRBotKkx +YYJQ7oS0u/n9gUbz45nR1DWXS3nAMCAXmNc6GLdFpjnTm8ivnnJi+8CBQvhUAKjK +BLKkOVFpTnDc8ha0ovtqCuKG2Ou+lU3z7BLqRCIGBbsHGxIC/aT708xGR9PhvARa +/y5NvdeDylnjX9pqa/zZXnO2844UfjQkXiJ5VP07MA0z4cQ56Isp1/UbT3+KCjRz +GT0HIE7AJhamgNE46ZHfhQfXSYhKxxPW3fd9wrFs55/1wTskfJgQFRYsrROZ6eDA +NyC8CV8reE/fgQk+0N/06lQ0/apqlEOC1uhNnS7b3AX7dk16BJNHsoMFs2SuDbOa +4skeCGK0oNjrbhYH9HIPQYoPEweE8QnebzSnxf9SRz0NlqS3vqsSEnIYY9F1t05u +YiNEZC4dk3vwRn6u3Br64fyg+dpYDWn+iSSWBan5Qof7uPht7QbZKCjLTRJcJVp2 +lwUw2p8yaABLqSgEZ23jH5glGX3dJ1itAxiYxcFy/8GAbd+qTvaco73nRCS7ZeL+ +FTrAh+xquPCw1yhbkeFtSVuUUqxQeXi9Zyq6kbeX+56HabAWPr3bg43zecFMM4tK +7xOA2p/9gala79mMYX49kYXwiP82nfouyyeSKv2jI9+6lejo+s4Lpnj6HfDsiJhl +Rw== +-----END ENCRYPTED PRIVATE KEY----- +"; + const string spki = "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfBYU9c0n28u02N/XCJY8yIsRqRVO5Zw+6kDHCremt2flHT4AaWnwGLAG9YyQJbRTvWN9nW2LK7Pv3uoIlvUSTnZEP0SXB5oZeqtxUdi6tuvcyqTIfsUSanLQucYITq8Qw3IMBzk+KpWNm98g9A/Xy30MkUS8mrBIO9pHmIZa55fvclDkTvLxjnGWA2avaBfJvHgMSTu0D2CQcmJrvwyKMhLCSIbQewZd2V7vc6gtxbRovKlrIwDTmDBXbfjbLljOuzg2yBLyYxXlozO9blpttbnOpU4kTspUVJXglmjsv7YSIJS3UKt3544l/srHbqlwC5CgOgjlwNfYPadO8kmBfAgMBAAE="; + var password = "password".ToCharArray(); + + await _keyStoreService.RemoveAsync("jsipfs"); + var key = await _keyStoreService.ImportAsync("jsipfs", pem, password); + Assert.AreEqual("jsipfs", key.Name); + Assert.AreEqual("QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb", key.Id.ToString()); + + var pubKey = await _keyStoreService.GetDfsPublicKeyAsync("jsipfs"); + Assert.AreEqual(spki, pubKey); + } + + [Test] + public void Import_Bad_Format() + { + const string pem = @"this is not PEM"; + var password = "password".ToCharArray(); + ExceptionAssert.Throws(() => + { + var _ = _keyStoreService.ImportAsync("bad", pem, password).Result; + }); + } + + [Test] + public void Import_Corrupted_Format() + { + const string pem = @"-----BEGIN ENCRYPTED PRIVATE KEY----- +MIIFDTA/BgkqhkiG9w0BBQ0wMjAaBgkqhkiG9w0BBQwwDQQILdGJynKmkrMCAWQw +-----END ENCRYPTED PRIVATE KEY----- +"; + var password = "password".ToCharArray(); + ExceptionAssert.Throws(() => + { + var _ = _keyStoreService.ImportAsync("bad", pem, password).Result; + }); + } + + [Test] + public async Task Create_RSA_Key() + { + const string name = "net-engine-test-create"; + var key = await _keyStoreService.CreateAsync(name, "rsa", 512); + try + { + Assert.NotNull(key); + Assert.NotNull(key.Id); + Assert.AreEqual(name, key.Name); + + var keys = await _keyStoreService.ListAsync(); + var clone = keys.Single(k => k.Name == name); + Assert.AreEqual(key.Name, clone.Name); + Assert.AreEqual(key.Id, clone.Id); + } + finally + { + await _keyStoreService.RemoveAsync(name); + } + } + + [Test] + public async Task Remove_Key() + { + const string name = "net-engine-test-remove"; + var key = await _keyStoreService.CreateAsync(name, "ed25519", 0); + var keys = await _keyStoreService.ListAsync(); + var clone = keys.Single(k => k.Name == name); + Assert.NotNull(clone); + + var removed = await _keyStoreService.RemoveAsync(name); + Assert.NotNull(removed); + Assert.AreEqual(key.Name, removed.Name); + Assert.AreEqual(key.Id, removed.Id); + + keys = await _keyStoreService.ListAsync(); + Assert.False(keys.Any(k => k.Name == name)); + } + + [Test] + public async Task Rename_Key() + { + const string name = "net-engine-test-rename0"; + const string newName = "net-engine-test-rename1"; + + await _keyStoreService.RemoveAsync(name); + await _keyStoreService.RemoveAsync(newName); + var key = await _keyStoreService.CreateAsync(name, "ed25519", 0); + var renamed = await _keyStoreService.RenameAsync(name, newName); + Assert.AreEqual(key.Id, renamed.Id); + Assert.AreEqual(newName, renamed.Name); + + var keys = await _keyStoreService.ListAsync(); + var enumerable = keys as IKey[] ?? keys.ToArray(); + Assert.True(enumerable.Any(k => k.Name == newName)); + Assert.False(enumerable.Any(k => k.Name == name)); + } + + [Test] + public async Task Remove_Unknown_Key() + { + const string name = "net-engine-test-remove-unknown"; + + var removed = await _keyStoreService.RemoveAsync(name); + Assert.Null(removed); + } + + [Test] + public async Task Rename_Unknown_Key() + { + const string name = "net-engine-test-rename-unknown"; + + var renamed = await _keyStoreService.RenameAsync(name, "foobar"); + Assert.Null(renamed); + } + + [Test] + public void Create_Unknown_KeyType() + { + ExceptionAssert.Throws(() => + { + var _ = _keyStoreService.CreateAsync("unknown", "unknown", 0).Result; + }); + } + + [Test] + public async Task UnsafeKeyName() + { + const string name = "../../../../../../../foo.key"; + var key = await _keyStoreService.CreateAsync(name, "ed25519", 0); + try + { + Assert.NotNull(key); + Assert.NotNull(key.Id); + Assert.AreEqual(name, key.Name); + + var keys = await _keyStoreService.ListAsync(); + var clone = keys.Single(k => k.Name == name); + Assert.AreEqual(key.Name, clone.Name); + Assert.AreEqual(key.Id, clone.Id); + } + finally + { + await _keyStoreService.RemoveAsync(name); + } + } + + [Test] + public async Task Create_Ed25519_Key() + { + const string name = "test-ed25519"; + var key = await _keyStoreService.CreateAsync(name, "ed25519", 0); + try + { + Assert.NotNull(key); + Assert.NotNull(key.Id); + Assert.AreEqual(name, key.Name); + + var keys = await _keyStoreService.ListAsync(); + var clone = keys.Single(k => k.Name == name); + Assert.AreEqual(key.Name, clone.Name); + Assert.AreEqual(key.Id, clone.Id); + + var priv = await _keyStoreService.GetPrivateKeyAsync(name); + Assert.NotNull(priv); + var pub = await _keyStoreService.GetDfsPublicKeyAsync(name); + Assert.NotNull(pub); + + // Verify key can be used as peer ID. + var peer = new Peer + { + Id = key.Id, + PublicKey = pub + }; + Assert.True(peer.IsValid()); + } + finally + { + await _keyStoreService.RemoveAsync(name); + } + } + + [Test] + public async Task Ed25519_Id_IdentityHash_of_PublicKey() + { + const string name = "test-ed25519-id-hash"; + var key = await _keyStoreService.CreateAsync(name, "ed25519", 0); + Assert.AreEqual("identity", key.Id.Algorithm.Name); + } + + [Test] + public async Task Import_OpenSSL_Ed25519() + { + // Created with: + // openssl genpkey -algorithm ED25519 -out k4.pem + // openssl pkcs8 -nocrypt -in k4.pem -topk8 -out k4.nocrypt.pem + const string pem = @"-----BEGIN PRIVATE KEY----- +MC4CAQAwBQYDK2VwBCIEIGJnyy3U4ksTQoRBz3mf1dxeFDPXZBrwh7gD7SqMg+/i +-----END PRIVATE KEY----- +"; + + await _keyStoreService.RemoveAsync("oed1"); + var key = await _keyStoreService.ImportAsync("oed1", pem); + Assert.AreEqual("oed1", key.Name); + Assert.AreEqual("18n3naE9kBZoVvgYMV6saMZe3jn87dZiNbQ22BhxKTwU5yUoGfvBL1R3eScjokDGBk7i", key.Id.ToString()); + + var privateKey = await _keyStoreService.GetPrivateKeyAsync("oed1"); + Assert.IsInstanceOf(typeof(Ed25519PrivateKeyParameters), privateKey); + } + } +} diff --git a/src/Catalyst.Abstractions/IO/Observers/IRpcRequestObserver.cs b/src/Catalyst.Core.Modules.Keystore.Tests/IntegrationTests/KeyStoreServiceTest.cs similarity index 83% rename from src/Catalyst.Abstractions/IO/Observers/IRpcRequestObserver.cs rename to src/Catalyst.Core.Modules.Keystore.Tests/IntegrationTests/KeyStoreServiceTest.cs index d54022bec9..0b8916d548 100644 --- a/src/Catalyst.Abstractions/IO/Observers/IRpcRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Keystore.Tests/IntegrationTests/KeyStoreServiceTest.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.Observers +namespace Catalyst.Core.Modules.Keystore.Tests.IntegrationTests { - public interface IRpcRequestObserver : IMessageObserver { } + public class KeyStoreServiceTest { } } diff --git a/src/Catalyst.Core.Modules.Keystore.Tests/IntegrationTests/LocalKeyStoreTests.cs b/src/Catalyst.Core.Modules.Keystore.Tests/IntegrationTests/LocalKeyStoreTests.cs deleted file mode 100644 index a79b0c1825..0000000000 --- a/src/Catalyst.Core.Modules.Keystore.Tests/IntegrationTests/LocalKeyStoreTests.cs +++ /dev/null @@ -1,151 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.Security.Authentication; -using System.Threading.Tasks; -using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.FileSystem; -using Catalyst.Abstractions.Keystore; -using Catalyst.Abstractions.Types; -using Catalyst.Core.Modules.Cryptography.BulletProofs; -using Catalyst.Core.Modules.Hashing; -using Catalyst.Protocol.Network; -using Catalyst.TestUtils; -using FluentAssertions; -using NSubstitute; -using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using Xunit.Abstractions; - -namespace Catalyst.Core.Modules.Keystore.Tests.IntegrationTests -{ - public sealed class LocalKeyStoreTests : FileSystemBasedTest - { - private readonly IFileSystem _fileSystem; - private readonly IKeyStore _keystore; - private readonly ICryptoContext _context; - private readonly IPasswordManager _passwordManager; - - public LocalKeyStoreTests(ITestOutputHelper output) : base(output) - { - _fileSystem = Substitute.For(); - _context = new FfiWrapper(); - - var logger = Substitute.For(); - _passwordManager = Substitute.For(); - _passwordManager.RetrieveOrPromptPassword(default) - .ReturnsForAnyArgs(TestPasswordReader.BuildSecureStringPassword("test password")); - - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - - _keystore = new LocalKeyStore(_passwordManager, - _context, - _fileSystem, - hashProvider, - logger); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public void KeyStore_Can_Generate_Key_And_Create_Keystore_File() - { - var privateKey = _keystore.KeyStoreGenerateAsync(NetworkType.Devnet, KeyRegistryTypes.DefaultKey); - privateKey.Should().NotBe(null); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public void KeyStore_Throws_Exception_On_Invalid_KeyStore_File() - { - _fileSystem.ReadTextFromCddSubDirectoryFile(Arg.Any(), Arg.Any()) - .Returns("bad contents"); - Assert.Throws(() => _keystore.KeyStoreDecrypt(KeyRegistryTypes.DefaultKey)); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public async Task Keystore_Can_Create_Keystore_File_From_Provided_Key() - { - string jsonKeyStore = null; - _fileSystem?.WriteTextFileToCddSubDirectoryAsync(Arg.Any(), Arg.Any(), - Arg.Do(x => jsonKeyStore = x)); - - var privateKey = _context.GeneratePrivateKey(); - await _keystore.KeyStoreEncryptAsync(privateKey, NetworkType.Devnet, KeyRegistryTypes.DefaultKey); - - _fileSystem?.ReadTextFromCddSubDirectoryFile(Arg.Any(), Arg.Any()) - .Returns(jsonKeyStore); - var storedKey = _keystore.KeyStoreDecrypt(KeyRegistryTypes.DefaultKey); - privateKey.Bytes.Should().BeEquivalentTo(storedKey.Bytes); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public async Task Keystore_Can_Create_Keystore_File_From_Key_It_Generates() - { - string jsonKeyStore = null; - await _fileSystem.WriteTextFileToCddSubDirectoryAsync(Arg.Any(), Arg.Any(), - Arg.Do(x => jsonKeyStore = x)); - - var privateKey = await _keystore.KeyStoreGenerateAsync(NetworkType.Devnet, KeyRegistryTypes.DefaultKey); - await _fileSystem.Received(1) - .WriteTextFileToCddSubDirectoryAsync(Arg.Any(), Arg.Any(), Arg.Any()); - - _fileSystem.ReadTextFromCddSubDirectoryFile(Arg.Any(), Arg.Any()) - .Returns(jsonKeyStore); - var storedKey = _keystore.KeyStoreDecrypt(KeyRegistryTypes.DefaultKey); - - storedKey.Should().NotBe(null); - privateKey.Bytes.Should().BeEquivalentTo(storedKey.Bytes); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public async Task Keystore_Throws_Exception_If_Password_Incorrect() - { - string jsonKeyStore = null; - await _fileSystem.WriteTextFileToCddSubDirectoryAsync(Arg.Any(), Arg.Any(), - Arg.Do(x => jsonKeyStore = x)); - - var privateKey = _context.GeneratePrivateKey(); - await _keystore.KeyStoreEncryptAsync(privateKey, NetworkType.Devnet, KeyRegistryTypes.DefaultKey); - - _fileSystem.ReadTextFromCddSubDirectoryFile(Arg.Any(), Arg.Any()) - .Returns(jsonKeyStore); - - _passwordManager.RetrieveOrPromptPassword(default) - .ReturnsForAnyArgs(t => TestPasswordReader.BuildSecureStringPassword("a different test password")); - - Assert.Throws(() => _keystore.KeyStoreDecrypt(KeyRegistryTypes.DefaultKey)); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] - public void Keystore_Returns_Null_If_Key_File_Doesnt_Exist() - { - _keystore.KeyStoreDecrypt(KeyRegistryTypes.DefaultKey).Should().Be(null); - } - } -} diff --git a/src/Catalyst.Core.Modules.Keystore/Catalyst.Core.Modules.Keystore.csproj b/src/Catalyst.Core.Modules.Keystore/Catalyst.Core.Modules.Keystore.csproj index 5cdd4942be..9ed82fa0db 100644 --- a/src/Catalyst.Core.Modules.Keystore/Catalyst.Core.Modules.Keystore.csproj +++ b/src/Catalyst.Core.Modules.Keystore/Catalyst.Core.Modules.Keystore.csproj @@ -1,19 +1,25 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Keystore - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Keystore.snk true + + 1701;1702;CS8002 + + + + - + diff --git a/src/Catalyst.Core.Modules.Keystore/InMemoryStore.cs b/src/Catalyst.Core.Modules.Keystore/InMemoryStore.cs new file mode 100644 index 0000000000..13eb9898cc --- /dev/null +++ b/src/Catalyst.Core.Modules.Keystore/InMemoryStore.cs @@ -0,0 +1,173 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Newtonsoft.Json; +using Nito.AsyncEx; + +namespace Catalyst.Core.Lib.FileSystem +{ + /// + /// A file based repository for name value pairs. + /// + /// + /// The type used for a unique name. + /// + /// + /// The type used for the value. + /// + /// + /// All operations are atomic, a reader/writer lock is used. + /// + public class InMemoryStore : IStore + where TValue : class + { + private IDictionary _memoryStore = new Dictionary(); + + /// + /// The fully qualififed path to a directory + /// that stores the name value pairs. + /// + /// + /// A fully qualified path. + /// + /// + /// The directory must already exist. + /// + public string Folder { get; set; } + + /// + /// A function that converts the name to a case insensitive key name. + /// + public Func NameToKey { get; set; } + + /// + /// A function that converts the case insensitive key to a name. + /// + public Func KeyToName { get; set; } + + public IEnumerable Values => _memoryStore.Values; + + /// + /// Try to get the value with the specified name. + /// + /// + /// The unique name of the entity. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a or null if the + /// does not exist. + /// + public async Task TryGetAsync(TName name, CancellationToken cancel = default) + { + if (_memoryStore.ContainsKey(name)) + { + return _memoryStore[name]; + } + + return null; + } + + /// + /// Get the value with the specified name. + /// + /// + /// The unique name of the entity. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a + /// + /// + /// When the does not exist. + /// + public async Task GetAsync(TName name, CancellationToken cancel = default) + { + var value = await TryGetAsync(name, cancel).ConfigureAwait(false); + if (value == null) + { + throw new KeyNotFoundException($"Missing '{name}'."); + } + + return value; + } + + /// + /// Put the value with the specified name. + /// + /// + /// The unique name of the entity. + /// + /// + /// The entity. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// If already exists, it's value is overwriten. + /// + /// The file is deleted if an exception is encountered. + /// + /// + public async Task PutAsync(TName name, TValue value, CancellationToken cancel = default) + { + _memoryStore.Add(name, value); + } + + /// + /// Remove the value with the specified name. + /// + /// + /// The unique name of the entity. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// A non-existent does nothing. + /// + public async Task RemoveAsync(TName name, CancellationToken cancel = default) + { + _memoryStore.Remove(name); + } + } +} diff --git a/src/Catalyst.Core.Modules.Keystore/KeyApi.cs b/src/Catalyst.Core.Modules.Keystore/KeyApi.cs new file mode 100644 index 0000000000..2d60ad2d4a --- /dev/null +++ b/src/Catalyst.Core.Modules.Keystore/KeyApi.cs @@ -0,0 +1,114 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Security; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Keystore; +using Org.BouncyCastle.Crypto; + +namespace Catalyst.Core.Modules.Keystore +{ + public sealed class KeyApi : IKeyApi + { + private readonly IKeyStoreService _keyStoreService; + + public KeyApi(IKeyStoreService keyStoreService) + { + _keyStoreService = keyStoreService; + } + + public async Task CreateAsync(string name, + string keyType, + int size, + CancellationToken cancel = default) + { + return await _keyStoreService.CreateAsync(name, keyType, size, cancel).ConfigureAwait(false); + } + + public async Task ExportAsync(string name, + char[] password, + CancellationToken cancel = default) + { + return await _keyStoreService.ExportAsync(name, password, cancel).ConfigureAwait(false); + } + + public async Task ImportAsync(string name, string pem, char[] password, CancellationToken cancel) + { + return await _keyStoreService.ImportAsync(name, pem, password, cancel).ConfigureAwait(false); + } + + public async Task GetKeyAsync(string keyName) + { + return await _keyStoreService.FindKeyByNameAsync(keyName).ConfigureAwait(false); + } + + public async Task GetDfsPublicKeyAsync(string name, CancellationToken cancel = default) + { + return await _keyStoreService.GetDfsPublicKeyAsync(name, cancel).ConfigureAwait(false); + } + + public async Task GetPrivateKeyAsync(string privateKeyName) + { + return await _keyStoreService.GetPrivateKeyAsync(privateKeyName).ConfigureAwait(false); + } + + public async Task CreateProtectedDataAsync(string keyName, + byte[] plainText, + CancellationToken cancel = default) + { + return await _keyStoreService.CreateProtectedDataAsync(keyName, plainText, cancel); + } + + public async Task ReadProtectedDataAsync(byte[] cipherText, + CancellationToken cancel = default) + { + return await _keyStoreService.ReadProtectedDataAsync(cipherText, cancel: cancel); + } + + public async Task> ListAsync(CancellationToken cancel = default) + { + return await _keyStoreService.ListAsync(cancel).ConfigureAwait(false); + } + + public async Task RemoveAsync(string name, CancellationToken cancel = default) + { + return await _keyStoreService.RemoveAsync(name, cancel).ConfigureAwait(false); + } + + public async Task RenameAsync(string oldName, + string newName, + CancellationToken cancel = default) + { + return await _keyStoreService.RenameAsync(oldName, newName, cancel).ConfigureAwait(false); + } + + public async Task SetPassphraseAsync(SecureString passphrase, + CancellationToken cancel = default) + { + await _keyStoreService.SetPassphraseAsync(passphrase, cancel); + } + } +} diff --git a/src/Catalyst.Core.Modules.Keystore/KeyFileStore.cs b/src/Catalyst.Core.Modules.Keystore/KeyFileStore.cs new file mode 100644 index 0000000000..bc6d6bf19f --- /dev/null +++ b/src/Catalyst.Core.Modules.Keystore/KeyFileStore.cs @@ -0,0 +1,48 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.Cryptography; +using Catalyst.Core.Lib.FileSystem; +using MultiFormats; +using System.IO; +using System.Text; + +namespace Catalyst.Core.Modules.Keystore +{ + public class KeyFileStore : FileStore + { + public KeyFileStore(RepositoryOptions repositoryOptions) + { + var folder = Path.Combine(repositoryOptions.Folder, "keys"); + if (!Directory.Exists(folder)) + { + Directory.CreateDirectory(folder); + } + + Folder = folder; + NameToKey = name => Encoding.UTF8.GetBytes(name).ToBase32(); + KeyToName = key => Encoding.UTF8.GetString(Base32.Decode(key)); + } + } +} diff --git a/src/Catalyst.Core.Modules.Keystore/KeyInfo.cs b/src/Catalyst.Core.Modules.Keystore/KeyInfo.cs new file mode 100644 index 0000000000..321535e513 --- /dev/null +++ b/src/Catalyst.Core.Modules.Keystore/KeyInfo.cs @@ -0,0 +1,34 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Cryptography; +using MultiFormats; + +namespace Catalyst.Core.Modules.Keystore +{ + internal class KeyInfo : IKey + { + public string Name { get; set; } + public MultiHash Id { get; set; } + } +} diff --git a/src/Catalyst.Core.Modules.Keystore/KeyStoreService.cs b/src/Catalyst.Core.Modules.Keystore/KeyStoreService.cs new file mode 100644 index 0000000000..a2051b18bc --- /dev/null +++ b/src/Catalyst.Core.Modules.Keystore/KeyStoreService.cs @@ -0,0 +1,801 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib.Cryptography; +using Catalyst.Core.Lib.Cryptography.Proto; +using Catalyst.Core.Lib.FileSystem; +using Common.Logging; +using MultiFormats; +using Org.BouncyCastle.Asn1; +using Org.BouncyCastle.Asn1.EdEC; +using Org.BouncyCastle.Asn1.Pkcs; +using Org.BouncyCastle.Asn1.Sec; +using Org.BouncyCastle.Asn1.X509; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Cms; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Digests; +using Org.BouncyCastle.Crypto.Generators; +using Org.BouncyCastle.Crypto.Operators; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Math; +using Org.BouncyCastle.OpenSsl; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.X509; +using ProtoBuf; + +namespace Catalyst.Core.Modules.Keystore +{ + /// + /// A secure key chain. + /// + public class KeyStoreService : IKeyStoreService + { + private static readonly ILog Log = LogManager.GetLogger(typeof(KeyStoreService)); + + private char[] _dek; + private IStore _store; + + /// + /// Create a new instance of the class. + /// + /// + public KeyStoreService(KeyChainOptions keyChainOptions, IStore store) + { + _store = store; + Options = keyChainOptions; + } + + /// + /// The configuration options. + /// + public KeyChainOptions Options { get; set; } + + /// + /// Encrypt data as CMS protected data. + /// + /// + /// The key name to protect the with. + /// + /// + /// The data to protect. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the cipher text of the . + /// + /// + /// Cryptographic Message Syntax (CMS), aka PKCS #7 and + /// RFC 5652, + /// describes an encapsulation syntax for data protection. It + /// is used to digitally sign, digest, authenticate, and/or encrypt + /// arbitrary message content. + /// + public async Task CreateProtectedDataAsync(string keyName, + byte[] plainText, + CancellationToken cancel = default) + { + // Identify the recipient by the Subject Key ID. + + // TODO: Need a method to just the get BC public key + // Get the BC key pair for the named key. + var ekey = await _store.TryGetAsync(keyName, cancel).ConfigureAwait(false); + if (ekey == null) + { + throw new KeyNotFoundException($"The key '{keyName}' does not exist."); + } + + AsymmetricCipherKeyPair kp = null; + UseEncryptedKey(ekey, key => { kp = GetKeyPairFromPrivateKey(key); }); + + // Add recipient type based on key type. + CmsEnvelopedDataGenerator edGen = new(); + switch (kp.Private) + { + case RsaPrivateCrtKeyParameters _: + edGen.AddKeyTransRecipient(kp.Public, Base58.Decode(ekey.Id)); + break; + case ECPrivateKeyParameters _: + { + var cert = await CreateBcCertificateAsync(keyName, cancel).ConfigureAwait(false); + edGen.AddKeyAgreementRecipient( + CmsEnvelopedGenerator.ECDHSha1Kdf, + kp.Private, + kp.Public, + cert, + CmsEnvelopedGenerator.Aes256Wrap + ); + break; + } + case Ed25519PrivateKeyParameters _: + var cert2 = await CreateBcCertificateAsync(keyName, cancel).ConfigureAwait(false); + edGen.AddKeyAgreementRecipient( + CmsEnvelopedGenerator.ECDHSha1Kdf, + kp.Private, + kp.Public, + cert2, + CmsEnvelopedGenerator.Aes256Wrap + ); + break; + default: + { + throw new NotSupportedException($"The key type {kp.Private.GetType().Name} is not supported."); + } + } + + // Generate the protected data. + var ed = edGen.Generate( + new CmsProcessableByteArray(plainText), + CmsEnvelopedGenerator.Aes256Cbc); + return ed.GetEncoded(); + } + + /// + /// Decrypt CMS protected data. + /// + /// + /// The protected CMS data. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the plain text byte array of the protected data. + /// + /// + /// When the required private key, to decrypt the data, is not foumd. + /// + /// + /// Cryptographic Message Syntax (CMS), aka PKCS #7 and + /// RFC 5652, + /// describes an encapsulation syntax for data protection. It + /// is used to digitally sign, digest, authenticate, and/or encrypt + /// arbitrary message content. + /// + public async Task ReadProtectedDataAsync(byte[] cipherText, + CancellationToken cancel = default) + { + CmsEnvelopedDataParser cms = new(cipherText); + + // Find a recipient whose key we hold. We only deal with recipient names + // issued by ipfs (O=ipfs, OU=keystore). + var knownKeys = (await ListAsync(cancel).ConfigureAwait(false)).ToArray(); + var recipient = cms.GetRecipientInfos().GetRecipients() + .OfType() + .Select(ri => + { + var kid = GetKeyId(ri); + var key = knownKeys.FirstOrDefault(k => k.Id == kid); + return new + { + recipient = ri, key + }; + }).FirstOrDefault(r => r.key != null); + + if (recipient == null) + { + throw new KeyNotFoundException("The required decryption key is missing."); + } + + // Decrypt the contents. + var decryptionKey = await GetPrivateKeyAsync(recipient.key.Name, cancel).ConfigureAwait(false); + return recipient.recipient.GetContent(decryptionKey); + } + + /// + /// Get the key ID for a recipient. + /// + /// + /// A recepient of the message. + /// + /// + /// The key ID of the recepient or null if the recepient info + /// is not understood or does not contain an IPFS key id. + /// + /// + /// The key ID is either the Subject Key Identifier (preferred) or the + /// issuer's distinguished name with the form "CN=<kid>,OU=keystore,O=ipfs". + /// + private MultiHash GetKeyId(RecipientInformation ri) + { + // Any errors are simply ignored. + try + { + // Subject Key Identifier is the key ID. + if (ri.RecipientID.SubjectKeyIdentifier is { } ski) + { + return new MultiHash(ski); + } + + // Issuer is CN=,OU=keystore,O=ipfs + var issuer = ri.RecipientID.Issuer; + if (issuer != null && issuer.GetValueList(X509Name.OU).Contains("keystore") + && issuer.GetValueList(X509Name.O).Contains("ipfs")) + { + var cn = issuer.GetValueList(X509Name.CN)[0] as string; + return new MultiHash(cn); + } + } + catch (Exception e) + { + Log.Warn("Failed reading CMS recipient info.", e); + } + + return null; + } + + /// + /// Sets the passphrase for the key chain. + /// + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// When the is wrong. + /// + /// + /// The is used to generate a DEK (derived encryption + /// key). The DEK is then used to encrypt the stored keys. + /// + /// Neither the nor the DEK are stored. + /// + /// + public async Task SetPassphraseAsync(SecureString passphrase, + CancellationToken cancel = default) + { + // TODO: Verify DEK options. + // TODO: get digest based on Options.Hash + passphrase.UseSecretBytes(plain => + { + Pkcs5S2ParametersGenerator pdb = new(new Sha256Digest()); + pdb.Init( + plain, + Encoding.UTF8.GetBytes(Options.Dek.Salt), + Options.Dek.IterationCount); + var key = (KeyParameter) pdb.GenerateDerivedMacParameters(Options.Dek.KeyLength * 8); + _dek = key.GetKey().ToBase64NoPad().ToCharArray(); + }); + + // Verify that that pass phrase is okay, by reading a key. + var akey = await _store.TryGetAsync("self", cancel).ConfigureAwait(false); + if (akey != null) + { + try + { + UseEncryptedKey(akey, _ => { }); + } + catch (Exception e) + { + throw new UnauthorizedAccessException("The pass phrase is wrong.", e); + } + } + + Log.Debug("Pass phrase is okay"); + } + + /// + /// Find a key by its name. + /// + /// + /// The local name of the key. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// an or null if the the key is not defined. + /// + public async Task FindKeyByNameAsync(string name, CancellationToken cancel = default) + { + var key = await _store.TryGetAsync(name, cancel).ConfigureAwait(false); + if (key == null) + { + return null; + } + + return new KeyInfo + { + Id = key.Id, Name = key.Name + }; + } + + /// + /// Gets the IPFS encoded public key for the specified key. + /// + /// + /// The local name of the key. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the IPFS encoded public key. + /// + /// + /// The IPFS public key is the base-64 encoding of a protobuf encoding containing + /// a type and the DER encoding of the PKCS Subject Public Key Info. + /// + /// + public async Task GetDfsPublicKeyAsync(string name, CancellationToken cancel = default) + { + string result = null; + var ekey = await _store.TryGetAsync(name, cancel).ConfigureAwait(false); + if (ekey != null) + { + UseEncryptedKey(ekey, key => + { + var kp = GetKeyPairFromPrivateKey(key); + var spki = SubjectPublicKeyInfoFactory.CreateSubjectPublicKeyInfo(kp.Public).GetDerEncoded(); + // Add protobuf cruft. + var publicKey = new PublicKey + { + Data = spki, + Type = kp.Public switch + { + RsaKeyParameters _ => KeyType.Rsa, + Ed25519PublicKeyParameters _ => KeyType.Ed25519, + ECPublicKeyParameters _ => KeyType.Secp256K1, + _ => throw new NotSupportedException( + $"The key type {kp.Public.GetType().Name} is not supported.") + } + }; + + using MemoryStream ms = new(); + Serializer.Serialize(ms, publicKey); + result = Convert.ToBase64String(ms.ToArray()); + }); + } + + return result; + } + + /// + public async Task CreateAsync(string name, + string keyType, + int size, + CancellationToken cancel = default) + { + // Apply defaults. + if (string.IsNullOrWhiteSpace(keyType)) + { + keyType = Options.DefaultKeyType; + } + + if (size < 1) + { + size = Options.DefaultKeySize; + } + + keyType = keyType.ToLowerInvariant(); + + // Create the key pair. + Log.DebugFormat("Creating {0} key named '{1}'", keyType, name); + IAsymmetricCipherKeyPairGenerator g; + switch (keyType) + { + case "rsa": + g = GeneratorUtilities.GetKeyPairGenerator("RSA"); + g.Init(new RsaKeyGenerationParameters( + BigInteger.ValueOf(0x10001), new SecureRandom(), size, 25)); + break; + case "ed25519": + g = GeneratorUtilities.GetKeyPairGenerator("Ed25519"); + g.Init(new Ed25519KeyGenerationParameters(new SecureRandom())); + break; + case "secp256k1": + g = GeneratorUtilities.GetKeyPairGenerator("EC"); + g.Init(new ECKeyGenerationParameters(SecObjectIdentifiers.SecP256k1, new SecureRandom())); + break; + default: + throw new Exception($"Invalid key type '{keyType}'."); + } + + var keyPair = g.GenerateKeyPair(); + Log.Debug("Created key"); + + return await AddPrivateKeyAsync(name, keyPair, cancel).ConfigureAwait(false); + } + + /// + public async Task ExportAsync(string name, + char[] password, + CancellationToken cancel = default) + { + var pem = ""; + var key = await _store.GetAsync(name, cancel).ConfigureAwait(false); + UseEncryptedKey(key, pkey => + { + using StringWriter sw = new(); + var pkcs8 = new Pkcs8Generator(pkey, Pkcs8Generator.PbeSha1_3DES) + { + Password = password + }; + PemWriter pw = new(sw); + pw.WriteObject(pkcs8); + pw.Writer.Flush(); + pem = sw.ToString(); + }); + + return pem; + } + + /// + public async Task ImportAsync(string name, + string pem, + char[] password = null, + CancellationToken cancel = default) + { + AsymmetricKeyParameter key; + using (StringReader sr = new(pem)) + { + using var pf = new PasswordFinder + { + Password = password + }; + PemReader reader = new(sr, pf); + try + { + key = reader.ReadObject() as AsymmetricKeyParameter; + } + catch (Exception e) + { + throw new UnauthorizedAccessException("The password is wrong.", e); + } + + if (key == null || !key.IsPrivate) + { + throw new InvalidDataException("Not a valid PEM private key"); + } + } + + return await AddPrivateKeyAsync(name, GetKeyPairFromPrivateKey(key), cancel).ConfigureAwait(false); + } + + /// + public Task> ListAsync(CancellationToken cancel = default) + { + var keys = _store.Values.Select(key => (IKey) new KeyInfo + { + Id = key.Id, Name = key.Name + }); + + return Task.FromResult(keys); + } + + /// + public async Task RemoveAsync(string name, CancellationToken cancel = default) + { + var key = await _store.TryGetAsync(name, cancel).ConfigureAwait(false); + if (key == null) + { + return null; + } + + await _store.RemoveAsync(name, cancel).ConfigureAwait(false); + return new KeyInfo + { + Id = key.Id, Name = key.Name + }; + } + + /// + public async Task RenameAsync(string oldName, + string newName, + CancellationToken cancel = default) + { + var key = await _store.TryGetAsync(oldName, cancel).ConfigureAwait(false); + if (key == null) + { + return null; + } + + key.Name = newName; + await _store.PutAsync(newName, key, cancel).ConfigureAwait(false); + await _store.RemoveAsync(oldName, cancel).ConfigureAwait(false); + + return new KeyInfo + { + Id = key.Id, Name = newName + }; + } + + /// + /// Gets the Bouncy Castle representation of the private key. + /// + /// + /// The local name of key. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the private key as an AsymmetricKeyParameter. + /// + public async Task GetPrivateKeyAsync(string name, CancellationToken cancel = default) + { + var key = await _store.TryGetAsync(name, cancel).ConfigureAwait(false); + if (key == null) + { + throw new KeyNotFoundException($"The key '{name}' does not exist."); + } + + AsymmetricKeyParameter kp = null; + UseEncryptedKey(key, pkey => { kp = pkey; }); + return kp; + } + + private void UseEncryptedKey(EncryptedKey key, Action action) + { + using StringReader sr = new(key.Pem); + using var pf = new PasswordFinder + { + Password = _dek + }; + PemReader reader = new(sr, pf); + var privateKey = (AsymmetricKeyParameter) reader.ReadObject(); + action(privateKey); + } + + private async Task AddPrivateKeyAsync(string name, + AsymmetricCipherKeyPair keyPair, + CancellationToken cancel) + { + // Create the key ID + var keyId = CreateKeyId(keyPair.Public); + + // Create the PKCS #8 container for the key + string pem; + await using (StringWriter sw = new()) + { + var pkcs8 = new Pkcs8Generator(keyPair.Private, Pkcs8Generator.PbeSha1_3DES) + { + Password = _dek + }; + PemWriter pw = new(sw); + pw.WriteObject(pkcs8); + await pw.Writer.FlushAsync(); + pem = sw.ToString(); + } + + // Store the key in the repository. + var key = new EncryptedKey + { + Id = keyId.ToBase58(), + Name = name, + Pem = pem + }; + await _store.PutAsync(name, key, cancel).ConfigureAwait(false); + Log.DebugFormat("Added key '{0}' with ID {1}", name, keyId); + + return new KeyInfo + { + Id = key.Id, Name = key.Name + }; + } + + /// + /// Create a key ID for the key. + /// + /// + /// + /// + /// The key id is the SHA-256 multihash of its public key. The public key is + /// a protobuf encoding containing a type and + /// the DER encoding of the PKCS SubjectPublicKeyInfo. + /// + public MultiHash CreateKeyId(AsymmetricKeyParameter key) + { + var spki = SubjectPublicKeyInfoFactory + .CreateSubjectPublicKeyInfo(key) + .GetDerEncoded(); + + // Add protobuf cruft. + var publicKey = new PublicKey + { + Data = spki + }; + switch (key) + { + case RsaKeyParameters _: + publicKey.Type = KeyType.Rsa; + break; + case ECPublicKeyParameters _: + publicKey.Type = KeyType.Secp256K1; + break; + case Ed25519PublicKeyParameters _: + publicKey.Type = KeyType.Ed25519; + break; + default: + throw new NotSupportedException($"The key type {key.GetType().Name} is not supported."); + } + + using (MemoryStream ms = new()) + { + Serializer.Serialize(ms, publicKey); + + // If the length of the serialized bytes <= 42, then we compute the "identity" multihash of + // the serialized bytes. The idea here is that if the serialized byte array + // is short enough, we can fit it in a multihash verbatim without having to + // condense it using a hash function. + var alg = ms.Length <= 48 ? "identity" : "sha2-256"; + + ms.Position = 0; + + return MultiHash.ComputeHash(ms, alg); + } + } + + private AsymmetricCipherKeyPair GetKeyPairFromPrivateKey(AsymmetricKeyParameter privateKey) + { + AsymmetricCipherKeyPair keyPair = null; + switch (privateKey) + { + case RsaPrivateCrtKeyParameters rsa: + { + RsaKeyParameters pub = new(false, rsa.Modulus, rsa.PublicExponent); + keyPair = new AsymmetricCipherKeyPair(pub, privateKey); + break; + } + case Ed25519PrivateKeyParameters ed: + { + var pub = ed.GeneratePublicKey(); + keyPair = new AsymmetricCipherKeyPair(pub, privateKey); + break; + } + case ECPrivateKeyParameters ec: + { + var q = ec.Parameters.G.Multiply(ec.D); + ECPublicKeyParameters pub = new(ec.AlgorithmName, q, ec.PublicKeyParamSet); + keyPair = new AsymmetricCipherKeyPair(pub, ec); + break; + } + } + + if (keyPair == null) + { + throw new NotSupportedException($"The key type {privateKey.GetType().Name} is not supported."); + } + + return keyPair; + } + + /// + /// Create a X509 certificate for the specified key. + /// + /// + /// The key name. + /// + /// + /// + public async Task CreateCertificateAsync(string keyName, + CancellationToken cancel = default) + { + var cert = await CreateBcCertificateAsync(keyName, cancel).ConfigureAwait(false); + return cert.GetEncoded(); + } + + /// + /// Create a X509 certificate for the specified key. + /// + /// + /// The key name. + /// + /// + /// + public async Task CreateBcCertificateAsync(string keyName, + CancellationToken cancel = default) + { + // Get the BC key pair for the named key. + var ekey = await _store.TryGetAsync(keyName, cancel).ConfigureAwait(false); + if (ekey == null) + { + throw new KeyNotFoundException($"The key '{keyName}' does not exist."); + } + + AsymmetricCipherKeyPair kp = null; + UseEncryptedKey(ekey, key => { kp = GetKeyPairFromPrivateKey(key); }); + + // A signer for the key. + KeyUsage ku = new(KeyUsage.DigitalSignature + | KeyUsage.DataEncipherment + | KeyUsage.KeyEncipherment); + ISignatureFactory signatureFactory = null; + switch (kp.Private) + { + case ECPrivateKeyParameters _: + signatureFactory = new Asn1SignatureFactory( + X9ObjectIdentifiers.ECDsaWithSha256.ToString(), + kp.Private); + break; + case RsaPrivateCrtKeyParameters _: + signatureFactory = new Asn1SignatureFactory( + PkcsObjectIdentifiers.Sha256WithRsaEncryption.ToString(), + kp.Private); + break; + case Ed25519PrivateKeyParameters _: + signatureFactory = new Asn1SignatureFactory( + EdECObjectIdentifiers.id_Ed25519.Id, + kp.Private); + ku = new KeyUsage(KeyUsage.DigitalSignature); + break; + } + + if (signatureFactory == null) + { + throw new NotSupportedException($"The key type {kp.Private.GetType().Name} is not supported."); + } + + // Build the certificate. + X509Name dn = new($"CN={ekey.Id}, OU=keystore, O=ipfs"); + SubjectKeyIdentifier ski = new(Base58.Decode(ekey.Id)); + + // Not a certificate authority. + // TODO: perhaps the "self" key is a CA and all other keys issued by it. + BasicConstraints bc = new(false); + + X509V3CertificateGenerator certGenerator = new(); + certGenerator.SetIssuerDN(dn); + certGenerator.SetSubjectDN(dn); + certGenerator.SetSerialNumber(BigInteger.ValueOf(1)); + certGenerator.SetNotAfter(DateTime.UtcNow.AddYears(10)); + certGenerator.SetNotBefore(DateTime.UtcNow); + certGenerator.SetPublicKey(kp.Public); + certGenerator.AddExtension(X509Extensions.SubjectKeyIdentifier, false, ski); + certGenerator.AddExtension(X509Extensions.BasicConstraints, true, bc); + certGenerator.AddExtension(X509Extensions.KeyUsage, false, ku); + + return certGenerator.Generate(signatureFactory); + } + + private sealed class PasswordFinder : IPasswordFinder, IDisposable + { + public char[] Password; + + public void Dispose() { Password = null; } + + public char[] GetPassword() { return Password; } + } + } +} diff --git a/src/Catalyst.Core.Modules.Keystore/KeystoreModule.cs b/src/Catalyst.Core.Modules.Keystore/KeystoreModule.cs index f7b34f2dcd..1b85af8ea7 100644 --- a/src/Catalyst.Core.Modules.Keystore/KeystoreModule.cs +++ b/src/Catalyst.Core.Modules.Keystore/KeystoreModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,6 +24,7 @@ using Autofac; using Catalyst.Abstractions.Keystore; using Catalyst.Core.Lib.Cryptography; +using Catalyst.Core.Lib.FileSystem; namespace Catalyst.Core.Modules.Keystore { @@ -31,10 +32,10 @@ public class KeystoreModule : Module { protected override void Load(ContainerBuilder builder) { - builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As>().SingleInstance(); } } } diff --git a/src/Catalyst.Core.Modules.Keystore/LocalKeyStore.cs b/src/Catalyst.Core.Modules.Keystore/LocalKeyStore.cs index 2831127a75..2b270df571 100644 --- a/src/Catalyst.Core.Modules.Keystore/LocalKeyStore.cs +++ b/src/Catalyst.Core.Modules.Keystore/LocalKeyStore.cs @@ -27,92 +27,72 @@ using System.Security.Authentication; using System.Threading.Tasks; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.Enumerator; -using Catalyst.Abstractions.FileSystem; using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.Keystore; using Catalyst.Abstractions.Types; -using Catalyst.Core.Lib.Config; -using Catalyst.Core.Lib.Extensions; -using Catalyst.Protocol.Account; using Catalyst.Protocol.Network; -using Nethereum.KeyStore; using Nethereum.KeyStore.Crypto; +using Org.BouncyCastle.Crypto.Parameters; using Serilog; -using TheDotNetLeague.MultiFormats.MultiBase; namespace Catalyst.Core.Modules.Keystore { - public sealed class LocalKeyStore : KeyStoreService, IKeyStore + public sealed class LocalKeyStore : IKeyStore { private readonly ILogger _logger; - private readonly IFileSystem _fileSystem; private readonly ICryptoContext _cryptoContext; private readonly IPasswordManager _passwordManager; - private readonly IHashProvider _hashProvider; + private readonly IKeyApi _keyApi; private readonly PasswordRegistryTypes _defaultNodePassword = PasswordRegistryTypes.DefaultNodePassword; private static int MaxTries => 5; public LocalKeyStore(IPasswordManager passwordManager, ICryptoContext cryptoContext, - IFileSystem fileSystem, - IHashProvider hashProvider, + IKeyApi keyApi, ILogger logger) { _passwordManager = passwordManager; _cryptoContext = cryptoContext; - _fileSystem = fileSystem; - _hashProvider = hashProvider; + _keyApi = keyApi; _logger = logger; } public IPrivateKey KeyStoreDecrypt(KeyRegistryTypes keyIdentifier) { - var json = GetJsonFromKeyStore(keyIdentifier); - if (string.IsNullOrEmpty(json)) + var keyGenerated = _keyApi.GetKeyAsync("self").ConfigureAwait(false).GetAwaiter().GetResult(); + if (keyGenerated == null) { - _logger.Error("No keystore exists for the given key"); return null; } - var keyBytes = KeyStoreDecrypt(_defaultNodePassword, json); - IPrivateKey privateKey = null; - try - { - privateKey = _cryptoContext.GetPrivateKeyFromBytes(keyBytes); - } - catch (ArgumentException) - { - _logger.Error("Keystore did not contain a valid key"); - } - - return privateKey; + var privateKeyBytes = KeyStoreDecrypt(PasswordRegistryTypes.DefaultNodePassword); + return _cryptoContext.GetPrivateKeyFromBytes(privateKeyBytes); } - private byte[] KeyStoreDecrypt(PasswordRegistryTypes passwordIdentifier, string json) + private byte[] KeyStoreDecrypt(PasswordRegistryTypes passwordIdentifier) { var tries = 0; while (tries < MaxTries) { - var securePassword = - _passwordManager.RetrieveOrPromptPassword(passwordIdentifier, "Please provide your node password"); - var password = StringFromSecureString(securePassword); + var passphrase = _passwordManager.RetrieveOrPromptAndAddPasswordToRegistry(PasswordRegistryTypes.DefaultNodePassword, "Please provide your DFS password"); + _keyApi.SetPassphraseAsync(passphrase).ConfigureAwait(false).GetAwaiter().GetResult(); try { - var keyBytes = DecryptKeyStoreFromJson(password, json); + var privateKey = (Ed25519PrivateKeyParameters) _keyApi.GetPrivateKeyAsync("self").ConfigureAwait(false).GetAwaiter().GetResult(); + var privateKeyBytes = privateKey.GetEncoded(); - if (keyBytes != null && keyBytes.Length > 0) + if (privateKeyBytes != null && privateKeyBytes.Length > 0) { - _passwordManager.AddPasswordToRegistry(passwordIdentifier, securePassword); - return keyBytes; + _passwordManager.AddPasswordToRegistry(passwordIdentifier, passphrase); + return privateKeyBytes; } } catch (DecryptionException) { - securePassword.Dispose(); + passphrase.Dispose(); _logger.Error("Error decrypting keystore"); } @@ -122,11 +102,18 @@ private byte[] KeyStoreDecrypt(PasswordRegistryTypes passwordIdentifier, string throw new AuthenticationException("Password incorrect for keystore."); } - public async Task KeyStoreGenerateAsync(NetworkType networkType, KeyRegistryTypes keyIdentifier) + public async Task KeyStoreGenerateAsync(KeyRegistryTypes keyIdentifier) { - var privateKey = _cryptoContext.GeneratePrivateKey(); + //var privateKey = _cryptoContext.GeneratePrivateKey(); + var passphrase = _passwordManager.RetrieveOrPromptAndAddPasswordToRegistry(PasswordRegistryTypes.DefaultNodePassword, "Please provide your DFS password"); - await KeyStoreEncryptAsync(privateKey, networkType, keyIdentifier).ConfigureAwait(false); + _keyApi.SetPassphraseAsync(passphrase).ConfigureAwait(false).GetAwaiter().GetResult(); + + var self = await _keyApi.CreateAsync("self", "ed25519", 0).ConfigureAwait(false); + + //await KeyStoreEncryptAsync(privateKey, networkType, keyIdentifier).ConfigureAwait(false); + + var privateKey = KeyStoreDecrypt(KeyRegistryTypes.DefaultKey); return privateKey; } @@ -137,29 +124,29 @@ public async Task KeyStoreEncryptAsync(IPrivateKey privateKey, { try { - var publicKeyHash = _hashProvider.ComputeMultiHash(privateKey.GetPublicKey().Bytes).ToArray() - .ToByteString(); - var address = new Address - { - PublicKeyHash = publicKeyHash, - AccountType = AccountType.PublicAccount, - NetworkType = networkType - }; + //var publicKeyHash = _hashProvider.ComputeMultiHash(privateKey.GetPublicKey().Bytes).ToArray() + // .ToByteString(); + //var address = new Address + //{ + // PublicKeyHash = publicKeyHash, + // AccountType = AccountType.PublicAccount, + // NetworkType = networkType + //}; - var securePassword = _passwordManager.RetrieveOrPromptPassword(_defaultNodePassword, - "Please create a password for this node"); + //var securePassword = _passwordManager.RetrieveOrPromptPassword(_defaultNodePassword, + // "Please create a password for this node"); - var password = StringFromSecureString(securePassword); + //var password = StringFromSecureString(securePassword); - var json = EncryptAndGenerateDefaultKeyStoreAsJson( - password, - _cryptoContext.ExportPrivateKey(privateKey), - address.RawBytes.ToBase32()); + //var json = EncryptAndGenerateDefaultKeyStoreAsJson( + // password, + // _cryptoContext.ExportPrivateKey(privateKey), + // address.RawBytes.ToBase32()); - _passwordManager.AddPasswordToRegistry(_defaultNodePassword, securePassword); + //_passwordManager.AddPasswordToRegistry(_defaultNodePassword, securePassword); - await _fileSystem.WriteTextFileToCddSubDirectoryAsync(keyIdentifier.Name, Constants.KeyStoreDataSubDir, - json); + //await _fileSystem.WriteTextFileToCddSubDirectoryAsync(keyIdentifier.Name, Constants.KeyStoreDataSubDir, + // json); } catch (Exception e) { @@ -167,11 +154,6 @@ await _fileSystem.WriteTextFileToCddSubDirectoryAsync(keyIdentifier.Name, Consta } } - private string GetJsonFromKeyStore(IEnumeration keyIdentifier) - { - return _fileSystem.ReadTextFromCddSubDirectoryFile(keyIdentifier.Name, Constants.KeyStoreDataSubDir); - } - private static string StringFromSecureString(SecureString secureString) { var stringPointer = Marshal.SecureStringToBSTR(secureString); diff --git a/src/Catalyst.Core.Modules.Keystore/SigningContextProvider.cs b/src/Catalyst.Core.Modules.Keystore/SigningContextProvider.cs index b10313aed6..65580c9c6b 100644 --- a/src/Catalyst.Core.Modules.Keystore/SigningContextProvider.cs +++ b/src/Catalyst.Core.Modules.Keystore/SigningContextProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Kvm.Tests/Catalyst.Core.Modules.Kvm.Tests.csproj b/src/Catalyst.Core.Modules.Kvm.Tests/Catalyst.Core.Modules.Kvm.Tests.csproj index 4ed4e25db7..7b3de3a5ed 100644 --- a/src/Catalyst.Core.Modules.Kvm.Tests/Catalyst.Core.Modules.Kvm.Tests.csproj +++ b/src/Catalyst.Core.Modules.Kvm.Tests/Catalyst.Core.Modules.Kvm.Tests.csproj @@ -1,15 +1,23 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Kvm.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Kvm.Tests.snk true - + + 1701;1702;VSTHRD200;CS8002 + - - + + + + + + + + diff --git a/src/Catalyst.Core.Modules.Kvm.Tests/IntegrationTests/CatalystVirtualMachineTests.cs b/src/Catalyst.Core.Modules.Kvm.Tests/IntegrationTests/CatalystVirtualMachineTests.cs index 1245fb96bb..db4ed0df6f 100644 --- a/src/Catalyst.Core.Modules.Kvm.Tests/IntegrationTests/CatalystVirtualMachineTests.cs +++ b/src/Catalyst.Core.Modules.Kvm.Tests/IntegrationTests/CatalystVirtualMachineTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,171 +23,324 @@ using System; using System.Linq; +using System.Text; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Core.Modules.Cryptography.BulletProofs; +using Catalyst.Core.Modules.Hashing; using Catalyst.TestUtils; using FluentAssertions; +using MultiFormats.Registry; using Nethermind.Core; +using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; -using Nethermind.Core.Json; using Nethermind.Core.Specs; +using Nethermind.Db; using Nethermind.Evm; using Nethermind.Evm.Tracing; +using Nethermind.Evm.Tracing.GethStyle; using Nethermind.Logging; -using Nethermind.Store; -using Xunit; -using Xunit.Abstractions; +using Nethermind.Serialization.Json; +using Nethermind.State; +using NUnit.Framework; +using IPrivateKey = Catalyst.Abstractions.Cryptography.IPrivateKey; namespace Catalyst.Core.Modules.Kvm.Tests.IntegrationTests { + [TestFixture] + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class CatalystVirtualMachineTests { - private readonly ITestOutputHelper _testOutputHelper; - public CatalystVirtualMachineTests(ITestOutputHelper testOutputHelper) { _testOutputHelper = testOutputHelper; } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Can_Run_Smoke_test() { var code = Bytes.FromHexString("0x600060000100"); GethLikeTxTracer txTracer = RunVirtualMachine(code); EthereumJsonSerializer serializer = new EthereumJsonSerializer(); - var trace = txTracer.BuildResult(); - _testOutputHelper.WriteLine(serializer.Serialize(trace, true)); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + + [Test] public void Can_Invoke_Range_Proof_Precompile() { var code = Bytes.FromHexString("0x60008080806201000062050000F400"); GethLikeTxTracer txTracer = RunVirtualMachine(code); EthereumJsonSerializer serializer = new EthereumJsonSerializer(); - var trace = txTracer.BuildResult(); - _testOutputHelper.WriteLine(serializer.Serialize(trace, true)); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); trace.Failed.Should().Be(false); trace.Entries.Last().Stack.Count.Should().Be(1); trace.Entries.Last().Stack.Last().Should().Be(VirtualMachine.BytesOne32.ToHexString()); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Can_Invoke_Origin() { var code = Bytes.FromHexString("0x3200"); GethLikeTxTracer txTracer = RunVirtualMachine(code); EthereumJsonSerializer serializer = new EthereumJsonSerializer(); - var trace = txTracer.BuildResult(); - _testOutputHelper.WriteLine(serializer.Serialize(trace, true)); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); trace.Failed.Should().Be(false); trace.Entries.Last().Stack.Count.Should().Be(1); trace.Entries.Last().Stack.Last().Should().Be(VirtualMachine.BytesZero32.ToHexString()); } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + + [Test] public void Can_Invoke_Address() { var code = Bytes.FromHexString("0x3000"); GethLikeTxTracer txTracer = RunVirtualMachine(code); EthereumJsonSerializer serializer = new EthereumJsonSerializer(); - var trace = txTracer.BuildResult(); - _testOutputHelper.WriteLine(serializer.Serialize(trace, true)); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); trace.Failed.Should().Be(false); trace.Entries.Last().Stack.Count.Should().Be(1); trace.Entries.Last().Stack.Last().Should().Be(VirtualMachine.BytesZero32.ToHexString()); } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + + [Test] public void Can_Invoke_Blockhash() { var code = Bytes.FromHexString("0x60014000"); Assert.Throws(() => RunVirtualMachine(code)); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Can_Invoke_Coinbase() { var code = Bytes.FromHexString("0x4100"); GethLikeTxTracer txTracer = RunVirtualMachine(code); EthereumJsonSerializer serializer = new EthereumJsonSerializer(); - var trace = txTracer.BuildResult(); - _testOutputHelper.WriteLine(serializer.Serialize(trace, true)); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); trace.Failed.Should().Be(false); trace.Entries.Last().Stack.Count.Should().Be(1); trace.Entries.Last().Stack.Last().Should().Be(VirtualMachine.BytesZero32.ToHexString()); } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + + [Test] public void Can_Invoke_Timestamp() { var code = Bytes.FromHexString("0x4200"); GethLikeTxTracer txTracer = RunVirtualMachine(code); EthereumJsonSerializer serializer = new EthereumJsonSerializer(); - var trace = txTracer.BuildResult(); - _testOutputHelper.WriteLine(serializer.Serialize(trace, true)); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); trace.Failed.Should().Be(false); trace.Entries.Last().Stack.Count.Should().Be(1); trace.Entries.Last().Stack.Last().Should().Be(VirtualMachine.BytesOne32.ToHexString()); } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + + [Test] public void Can_Invoke_Number() { var code = Bytes.FromHexString("0x4300"); GethLikeTxTracer txTracer = RunVirtualMachine(code); EthereumJsonSerializer serializer = new EthereumJsonSerializer(); - var trace = txTracer.BuildResult(); - _testOutputHelper.WriteLine(serializer.Serialize(trace, true)); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); trace.Failed.Should().Be(false); trace.Entries.Last().Stack.Count.Should().Be(1); trace.Entries.Last().Stack.Last().Should().Be(VirtualMachine.BytesOne32.ToHexString()); } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + + [Test] public void Can_Invoke_Difficulty() { var code = Bytes.FromHexString("0x4400"); GethLikeTxTracer txTracer = RunVirtualMachine(code); EthereumJsonSerializer serializer = new EthereumJsonSerializer(); - var trace = txTracer.BuildResult(); - _testOutputHelper.WriteLine(serializer.Serialize(trace, true)); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); trace.Failed.Should().Be(false); trace.Entries.Last().Stack.Count.Should().Be(1); trace.Entries.Last().Stack.Last().Should().Be(VirtualMachine.BytesOne32.ToHexString()); } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + + [Test] public void Can_Invoke_Gas_Limit() { var code = Bytes.FromHexString("0x4500"); GethLikeTxTracer txTracer = RunVirtualMachine(code); EthereumJsonSerializer serializer = new EthereumJsonSerializer(); - var trace = txTracer.BuildResult(); - _testOutputHelper.WriteLine(serializer.Serialize(trace, true)); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); trace.Failed.Should().Be(false); trace.Entries.Last().Stack.Count.Should().Be(1); trace.Entries.Last().Stack.Last().Should().Be("f4240".PadLeft(64, '0')); } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + + [Test] public void Can_Invoke_Chain_Id() { var code = Bytes.FromHexString("0x4600"); GethLikeTxTracer txTracer = RunVirtualMachine(code); EthereumJsonSerializer serializer = new EthereumJsonSerializer(); - var trace = txTracer.BuildResult(); - _testOutputHelper.WriteLine(serializer.Serialize(trace, true)); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); trace.Failed.Should().Be(false); trace.Entries.Last().Stack.Count.Should().Be(1); trace.Entries.Last().Stack.Last().Should().Be(VirtualMachine.BytesOne32.ToHexString()); } + + [Test] + public void Blake_precompile() + { + Address blakeAddress = Address.FromNumber(1 + KatVirtualMachine.CatalystPrecompilesAddressingSpace); + string addressCode = blakeAddress.Bytes.ToHexString(false); + var code = Bytes.FromHexString("0x602060006080600073" + addressCode + "45fa00"); + GethLikeTxTracer txTracer = RunVirtualMachine(code); + EthereumJsonSerializer serializer = new EthereumJsonSerializer(); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); + trace.Entries.Last().Stack.First().Should().Be("0000000000000000000000000000000000000000000000000000000000000001"); + trace.Entries.Last().Memory.First().Should().Be("378d0caaaa3855f1b38693c1d6ef004fd118691c95c959d4efa950d6d6fcf7c1"); + } + + [Test] + public void Ed25519_precompile_can_verify_correct_sig() + { + HashProvider hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + + FfiWrapper cryptoContext = new FfiWrapper(); + IPrivateKey signingPrivateKey = cryptoContext.GeneratePrivateKey(); + var signingPublicKeyBytes = signingPrivateKey.GetPublicKey().Bytes; + + var byteCode = PrepareEd25519PrecompileCall(hashProvider, cryptoContext, signingPrivateKey, signingPrivateKey); + + GethLikeTxTracer txTracer = RunVirtualMachine(byteCode); + EthereumJsonSerializer serializer = new EthereumJsonSerializer(); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); + trace.Entries.Last().Stack.First().Should().Be("0000000000000000000000000000000000000000000000000000000000000001"); + trace.Entries.Last().Memory.First().Should().StartWith("01"); + } + + [Test] + public void Ed25519_precompile_can_verify_incorrect_sig() + { + HashProvider hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + FfiWrapper cryptoContext = new FfiWrapper(); + IPrivateKey signingPrivateKey = cryptoContext.GeneratePrivateKey(); + IPrivateKey otherPrivateKey = cryptoContext.GeneratePrivateKey(); + Assert.AreNotEqual(signingPrivateKey, otherPrivateKey); + + var byteCode = PrepareEd25519PrecompileCall(hashProvider, cryptoContext, signingPrivateKey, otherPrivateKey); + + GethLikeTxTracer txTracer = RunVirtualMachine(byteCode); + EthereumJsonSerializer serializer = new EthereumJsonSerializer(); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); + trace.Entries.Last().Stack.First().Should().Be("0000000000000000000000000000000000000000000000000000000000000001"); + trace.Entries.Last().Memory.First().Should().StartWith("00"); + } + + [Test] + public void Ed25519_precompile_can_report_too_short_input() + { + var byteCode = Bytes.FromHexString( + + // PUSH1 32 PUSH1 0 PUSH1 128 PUSH1 0 PUSH20 address GAS STATICCALL + // make a call to precompile and pass invalid [0,128) bytes of memory as an input + // and store result at [0,1) of memory array + // allow precompile to use all the gas required + "600160006080600073" + + GetEd25519PrecompileAddressAsHex() + + "45fa00"); + + GethLikeTxTracer txTracer = RunVirtualMachine(byteCode); + EthereumJsonSerializer serializer = new EthereumJsonSerializer(); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); + trace.Entries.Last().Stack.First().Should().Be("0000000000000000000000000000000000000000000000000000000000000000"); + } + [Test] + public void Ed25519_precompile_can_report_too_long_input() + { + var byteCode = Bytes.FromHexString( + + // PUSH1 32 PUSH1 0 PUSH1 192 PUSH1 0 PUSH20 address GAS STATICCALL + // make a call to precompile and pass invalid [0,192) bytes of memory as an input + // and store result at [0,1) of memory array + // allow precompile to use all the gas required + "6001600060c0600073" + + GetEd25519PrecompileAddressAsHex() + + "45fa00"); + + GethLikeTxTracer txTracer = RunVirtualMachine(byteCode); + EthereumJsonSerializer serializer = new EthereumJsonSerializer(); + GethLikeTxTrace trace = txTracer.BuildResult(); + TestContext.WriteLine(serializer.Serialize(trace, true)); + trace.Entries.Last().Stack.First().Should().Be("0000000000000000000000000000000000000000000000000000000000000000"); + } + + private byte[] PrepareEd25519PrecompileCall(HashProvider hashProvider, FfiWrapper cryptoContext, IPrivateKey signingPrivateKey, IPrivateKey otherPrivateKey) + { + var data = Encoding.UTF8.GetBytes("Testing testing 1 2 3"); + var message = hashProvider.ComputeMultiHash(data).Digest; + + var signingContext = Encoding.UTF8.GetBytes("Testing testing 1 2 3 context"); + var context = hashProvider.ComputeMultiHash(signingContext).Digest; + + ISignature signature = cryptoContext.Sign(signingPrivateKey, message, context); + var signatureBytes = signature.SignatureBytes; + var publicKeyBytes = otherPrivateKey.GetPublicKey().Bytes; + + // below verify check is not needed but allows for greater confidence for any person in the future + // that would approach and debug test when it goes wrong + // ===================================================== + cryptoContext.Verify(signature, message, context) + .Should().BeTrue("signature generated with private key should verify with corresponding public key"); + + // ===================================================== + + // save message hash in memory at position 0x00 + string pushToMemoryAt0 = "7f" + message.ToHexString(false) + "600052"; + + // save first 32 bytes of the sig in memory starting from position 0x20 + string pushToMemoryAt32 = "7f" + signatureBytes.AsSpan(0, 32).ToHexString(false) + "602052"; + + // save remaining 32 bytes of the sig in memory starting from position 0x40 + string pushToMemoryAt64 = "7f" + signatureBytes.AsSpan(32, 32).ToHexString(false) + "604052"; + + // save context bytes in memory starting from position 0x60 (but this should be changed if context is smaller) + string pushToMemoryAt96 = "7f" + context.ToHexString(false) + "606052"; + + // save public key bytes in memory starting from position 0x80 (but this should be changed if context is smaller) + string pushToMemoryAt128 = "7f" + publicKeyBytes.ToHexString(false) + "608052"; + + // address of the precompile within Catalyst + string addressCode = GetEd25519PrecompileAddressAsHex(); + + var byteCode = Bytes.FromHexString( + pushToMemoryAt0 + + pushToMemoryAt32 + + pushToMemoryAt64 + + pushToMemoryAt96 + + pushToMemoryAt128 + + + // PUSH1 32 PUSH1 0 PUSH1 160 PUSH1 0 PUSH20 address GAS STATICCALL + // make a call to precompile and pass [0,160) bytes of memory as an input + // and store result at [0,1) of memory array + // allow precompile to use all the gas required + "6001600060a0600073" + + addressCode + + "45fa00"); + + TestContext.WriteLine(byteCode.ToHexString()); + return byteCode; + } + + private static string GetEd25519PrecompileAddressAsHex() + { + Address edAddress = Address.FromNumber(2 + KatVirtualMachine.CatalystPrecompilesAddressingSpace); + string addressCode = edAddress.Bytes.ToHexString(false); + return addressCode; + } + /* * also need to test: * BALANCE @@ -215,22 +368,27 @@ private static GethLikeTxTracer RunVirtualMachine(byte[] code) // these values will be set by the tx processor within the state update logic ExecutionEnvironment env = new ExecutionEnvironment(); - env.Originator = Address.Zero; // tx sender - env.Sender = Address.Zero; // sender of this call for a given call depth - env.ExecutingAccount = Address.Zero; // account in which context the code is executed, it may be different from the code source when invoking a lib - env.Value = 1.Kat(); // sometimes the value is just a description of an upper level call to be used by a an invoke library method - env.TransferValue = 1.Kat(); // this is the actual value transferred from sender to recipient - env.GasPrice = 0; // conversion from gas units to FULs - env.InputData = new byte[0]; // only needed for contracts requiring input (ensure that this is not limited to 60bytes) - env.CallDepth = 0; // zero when starting tx + env.Originator = Address.Zero; // tx sender + env.Sender = Address.Zero; // sender of this call for a given call depth + env.ExecutingAccount = + Address.Zero; // account in which context the code is executed, it may be different from the code source when invoking a lib + env.Value = 1 + .Kat(); // sometimes the value is just a description of an upper level call to be used by a an invoke library method + env.TransferValue = 1.Kat(); // this is the actual value transferred from sender to recipient + env.GasPrice = 0; // conversion from gas units to FULs + env.InputData = + new byte[0]; // only needed for contracts requiring input (ensure that this is not limited to 60bytes) + env.CallDepth = 0; // zero when starting tx StateUpdate stateUpdate = new StateUpdate(); // Catalyst single state update context (all phases together) - stateUpdate.Difficulty = 1; // some metric describing the state update that is relevant for consensus - stateUpdate.Number = 1; // state update sequence number - stateUpdate.Timestamp = 1; // state update T0 - stateUpdate.GasLimit = 1_000_000; // max gas units to be available for this tx inside the kvm - stateUpdate.GasBeneficiary = Address.Zero; // will get all the gas fees - stateUpdate.GasUsed = 0L; // zero if this is the first transaction in the update (accumulator over txs) + stateUpdate.Difficulty = + 1; // some metric describing the state update that is relevant for consensus + stateUpdate.Number = 1; // state update sequence number + stateUpdate.Timestamp = 1; // state update T0 + stateUpdate.GasLimit = 1_000_000; // max gas units to be available for this tx inside the kvm + stateUpdate.GasBeneficiary = Address.Zero; // will get all the gas fees + stateUpdate.GasUsed = + 0L; // zero if this is the first transaction in the update (accumulator over txs) env.CurrentBlock = stateUpdate; // this would be loaded by the tx processor from the recipient's code storage @@ -241,7 +399,14 @@ private static GethLikeTxTracer RunVirtualMachine(byte[] code) // this would be set by the tx processor that understands the type of transaction VmState vmState = new VmState(1_000_000L, env, ExecutionType.Transaction, false, true, false); - KatVirtualMachine virtualMachine = new KatVirtualMachine(stateProvider, storageProvider, stateUpdateHashProvider, specProvider, LimboLogs.Instance); + KatVirtualMachine virtualMachine = new KatVirtualMachine( + stateProvider, + storageProvider, + stateUpdateHashProvider, + specProvider, + new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")), + new FfiWrapper(), + LimboLogs.Instance); virtualMachine.Run(vmState, txTracer); return txTracer; } diff --git a/src/Catalyst.Core.Modules.Kvm.Tests/IntegrationTests/DeltaExecutorTests.cs b/src/Catalyst.Core.Modules.Kvm.Tests/IntegrationTests/DeltaExecutorTests.cs index b9f043f050..312e2f0d5e 100644 --- a/src/Catalyst.Core.Modules.Kvm.Tests/IntegrationTests/DeltaExecutorTests.cs +++ b/src/Catalyst.Core.Modules.Kvm.Tests/IntegrationTests/DeltaExecutorTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,303 +23,364 @@ using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.Kvm; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.Extensions.Protocol.Wire; using Catalyst.Core.Modules.Cryptography.BulletProofs; +using Catalyst.Core.Modules.Hashing; +using Catalyst.Protocol.Cryptography; +using Catalyst.Protocol.Network; using Catalyst.TestUtils; using Google.Protobuf; +using MultiFormats.Registry; using Nethermind.Core; using Nethermind.Core.Extensions; +using Nethermind.Db; +using Nethermind.Evm; using Nethermind.Evm.Tracing; using Nethermind.Logging; -using Nethermind.Store; +using Nethermind.State; using NSubstitute; using Serilog.Events; -using Xunit; +using NUnit.Framework; using ILogger = Serilog.ILogger; namespace Catalyst.Core.Modules.Kvm.Tests.IntegrationTests { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class DeltaExecutorTests { - private readonly ICryptoContext _cryptoContext = new FfiWrapper(); - private readonly CatalystSpecProvider _specProvider; - private readonly StateProvider _stateProvider; - private readonly IPublicKey _recipient; - private readonly IPublicKey _sender; - private readonly IPublicKey _poorSender; - private readonly DeltaExecutor _executor; + private ICryptoContext _cryptoContext = new FfiWrapper(); + private CatalystSpecProvider _specProvider; + private StateProvider _stateProvider; + private IPrivateKey _senderPrivateKey; + private IPublicKey _senderPublicKey; + private SigningContext _signingContext; + private IPublicKey _recipient; + private IPublicKey _poorSender; + private DeltaExecutor _executor; /** * @TODO this should extend file system based tests and resolve tests via autofac container */ - public DeltaExecutorTests() + [SetUp] + public void Init() { _specProvider = new CatalystSpecProvider(); _stateProvider = new StateProvider(new StateDb(), new StateDb(), LimboLogs.Instance); var storageProvider = new StorageProvider(new StateDb(), _stateProvider, LimboLogs.Instance); - IKvm virtualMachine = new KatVirtualMachine(_stateProvider, storageProvider, new StateUpdateHashProvider(), _specProvider, LimboLogs.Instance); + IKvm virtualMachine = new KatVirtualMachine(_stateProvider, storageProvider, new StateUpdateHashProvider(), + _specProvider, new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")), new FfiWrapper(), LimboLogs.Instance); var logger = Substitute.For(); logger.IsEnabled(Arg.Any()).Returns(true); - + + _senderPrivateKey = _cryptoContext.GeneratePrivateKey(); + _senderPublicKey = _senderPrivateKey.GetPublicKey(); + _recipient = _cryptoContext.GeneratePrivateKey().GetPublicKey(); - _sender = _cryptoContext.GeneratePrivateKey().GetPublicKey(); _poorSender = _cryptoContext.GeneratePrivateKey().GetPublicKey(); - + _stateProvider.CreateAccount(_poorSender.ToKvmAddress(), 0.Kat()); - _stateProvider.CreateAccount(_sender.ToKvmAddress(), 1000.Kat()); + _stateProvider.CreateAccount(_senderPublicKey.ToKvmAddress(), 1000.Kat()); _stateProvider.CreateAccount(Address.Zero, 1000.Kat()); _stateProvider.Commit(_specProvider.GenesisSpec); - - _executor = new DeltaExecutor(_specProvider, _stateProvider, storageProvider, virtualMachine, new FfiWrapper(), logger); + + _executor = new DeltaExecutor(_specProvider, _stateProvider, storageProvider, virtualMachine, + new FfiWrapper(), logger); + + _signingContext = new SigningContext + { + NetworkType = NetworkType.Devnet, + SignatureType = SignatureType.TransactionPublic + }; } - - [Fact] + + [Test] public void Fails_when_sender_not_specified() { - var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _sender, 3); - delta.ContractEntries[0].Base.SenderPublicKey = ByteString.Empty; + var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _senderPublicKey, 3); + delta.PublicEntries[0].SenderAddress = ByteString.Empty; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - + tracer.Received().MarkAsFailed(_recipient.ToKvmAddress(), 21000L, Bytes.Empty, "invalid"); } - - [Fact] + + [Test] public void Fails_when_gas_limit_below_data_intrinsic_cost() { - var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _sender, 0, "0x0102"); - delta.ContractEntries[0].GasLimit = 21001; // just above 21000 but not paying for data + var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _senderPublicKey, 0, "0x0102"); + delta.PublicEntries[0].GasLimit = 21001; // just above 21000 but not paying for data + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - + tracer.Received().MarkAsFailed(_recipient.ToKvmAddress(), 21001, Bytes.Empty, "invalid"); } - - [Fact] + + [Test] public void Fails_when_gas_limit_below_entry_intrinsic_cost() { - var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _sender, 0); - delta.ContractEntries[0].GasLimit = 20999; // just below 21000 + var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _senderPublicKey, 0); + delta.PublicEntries[0].GasLimit = 20999; // just below 21000 + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - + tracer.Received().MarkAsFailed(_recipient.ToKvmAddress(), 20999, Bytes.Empty, "invalid"); } - - [Fact] + + [Test] public void Fails_on_wrong_nonce() { - var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _sender, 0, "0x", 1); + var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _senderPublicKey, 0, "0x", 1); + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - + tracer.Received().MarkAsFailed(_recipient.ToKvmAddress(), 21000, Bytes.Empty, "invalid"); } - - [Fact] + + [Test] public void Fails_on_insufficient_balance() { var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _poorSender, 0); - + // when gas price is non-zero then sender needs to have a non-zero balance to pay for the gas cost - delta.ContractEntries[0].GasPrice = 1; + delta.PublicEntries[0].GasPrice = 1.ToUint256ByteString(); + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - + tracer.Received().MarkAsFailed(_recipient.ToKvmAddress(), 21000, Bytes.Empty, "invalid"); } - - [Fact] + + [Test] public void Fails_when_tx_beyond_delta_gas_limit() { - var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _sender, 0); - delta.ContractEntries[0].GasLimit = 10_000_000; + var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _senderPublicKey, 0); + delta.PublicEntries[0].GasLimit = 10_000_000; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - + tracer.Received().MarkAsFailed(_recipient.ToKvmAddress(), 10_000_000, Bytes.Empty, "invalid"); } - - [Fact] + + [Test] public void Can_deploy_code() { - var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _sender, 0, "0x60016000526001601FF300"); - delta.ContractEntries[0].GasLimit = 1_000_000L; - delta.ContractEntries[0].Base.ReceiverPublicKey = ByteString.Empty; + var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _senderPublicKey, 0, + "0x60016000526001601FF300"); + delta.PublicEntries[0].GasLimit = 1_000_000L; + delta.PublicEntries[0].ReceiverAddress = ByteString.Empty; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - - var contractAddress = Address.OfContract(_sender.ToKvmAddress(), 0); + + var contractAddress = ContractAddress.From(_senderPublicKey.ToKvmAddress(), 0); tracer.Received().MarkAsSuccess(contractAddress, 53370, Arg.Any(), Arg.Any()); } - - [Fact] + + [Test] public void Can_self_destruct() { - var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _sender, 0, "0x730001020304050607080910111213141516171819ff"); - delta.ContractEntries[0].GasLimit = 1_000_000L; - delta.ContractEntries[0].Base.ReceiverPublicKey = ByteString.Empty; + var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _senderPublicKey, 0, + "0x730001020304050607080910111213141516171819ff"); + delta.PublicEntries[0].GasLimit = 1_000_000L; + delta.PublicEntries[0].ReceiverAddress = ByteString.Empty; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - - var contractAddress = Address.OfContract(_sender.ToKvmAddress(), 0); + + var contractAddress = ContractAddress.From(_senderPublicKey.ToKvmAddress(), 0); tracer.Received().MarkAsSuccess(contractAddress, 34343, Arg.Any(), Arg.Any()); } - - [Fact] + + [Test] public void Fails_when_not_enough_gas_for_code_deposit() { - var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _sender, 0, "0x60016000526001601FF300"); - delta.ContractEntries[0].GasLimit = 53369; - delta.ContractEntries[0].Base.ReceiverPublicKey = ByteString.Empty; + var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _senderPublicKey, 0, + "0x60016000526001601FF300"); + delta.PublicEntries[0].GasLimit = 53369; + delta.PublicEntries[0].ReceiverAddress = ByteString.Empty; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - - var contractAddress = Address.OfContract(_sender.ToKvmAddress(), 0); + + var contractAddress = ContractAddress.From(_senderPublicKey.ToKvmAddress(), 0); tracer.Received().MarkAsFailed(contractAddress, 53369, Arg.Any(), null); } - - [Fact] + + [Test] public void Throws_on_theoretical_contract_crash() { - var contractAddress = Address.OfContract(_sender.ToKvmAddress(), 0); + var contractAddress = ContractAddress.From(_senderPublicKey.ToKvmAddress(), 0); _stateProvider.CreateAccount(contractAddress, 1000.Kat()); var codeHash = _stateProvider.UpdateCode(Bytes.FromHexString("0x01")); _stateProvider.UpdateCodeHash(contractAddress, codeHash, _specProvider.GenesisSpec); _stateProvider.Commit(_specProvider.GenesisSpec); - var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _sender, 0, "0x60016000526001601FF300"); - delta.ContractEntries[0].GasLimit = 1_000_000L; - delta.ContractEntries[0].Base.ReceiverPublicKey = ByteString.Empty; + var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _senderPublicKey, 0, + "0x60016000526001601FF300"); + delta.PublicEntries[0].GasLimit = 1_000_000L; + delta.PublicEntries[0].ReceiverAddress = ByteString.Empty; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); tracer.Received().MarkAsFailed(contractAddress, 1_000_000L, Arg.Any(), null); } - - [Fact] + + [Test] public void Update_storage_root_on_contract_clash() { - var contractAddress = Address.OfContract(_sender.ToKvmAddress(), 0); + var contractAddress = ContractAddress.From(_senderPublicKey.ToKvmAddress(), 0); _stateProvider.CreateAccount(contractAddress, 1000.Kat()); _stateProvider.Commit(_specProvider.GenesisSpec); - var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _sender, 0, "0x60016000526001601FF300"); - delta.ContractEntries[0].GasLimit = 1_000_000L; - delta.ContractEntries[0].Base.ReceiverPublicKey = ByteString.Empty; + var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _senderPublicKey, 0, + "0x60016000526001601FF300"); + delta.PublicEntries[0].GasLimit = 1_000_000L; + delta.PublicEntries[0].ReceiverAddress = ByteString.Empty; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); tracer.Received().MarkAsSuccess(contractAddress, 53370, Arg.Any(), Arg.Any()); } - - [Fact] + + [Test] public void Can_deploy_code_read_only() { - var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _sender, 0, "0x60016000526001601FF300"); - delta.ContractEntries[0].GasLimit = 1_000_000L; - delta.ContractEntries[0].Base.ReceiverPublicKey = ByteString.Empty; + var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _senderPublicKey, 0, + "0x60016000526001601FF300"); + delta.PublicEntries[0].GasLimit = 1_000_000L; + delta.PublicEntries[0].ReceiverAddress = ByteString.Empty; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - - _executor.CallAndRestore(delta, tracer); - var contractAddress = Address.OfContract(_sender.ToKvmAddress(), 0); + _executor.CallAndReset(delta, tracer); + + var contractAddress = ContractAddress.From(_senderPublicKey.ToKvmAddress(), 0); tracer.Received().MarkAsSuccess(contractAddress, 53370, Arg.Any(), Arg.Any()); } - - [Fact] + + [Test] public void Does_not_crash_on_kvm_error() { // here we test a case when we deploy a contract where constructor throws invalid opcode EVM error // 0xfe is a bad opcode that immediately causes an EVM error - var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _sender, 0, "0xfe"); - delta.ContractEntries[0].GasLimit = 1_000_000L; + var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _senderPublicKey, 0, "0xfe"); + delta.PublicEntries[0].GasLimit = 1_000_000L; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - - var contractAddress = Address.OfContract(_sender.ToKvmAddress(), 0); - tracer.Received().MarkAsFailed(contractAddress, 1_000_000L, Arg.Any(), "Error"); + + var contractAddress = ContractAddress.From(_senderPublicKey.ToKvmAddress(), 0); + tracer.Received().MarkAsFailed(contractAddress, 1_000_000L, Arg.Any(), "BadInstruction"); } - [Fact] + [Test] public void Does_not_crash_on_kvm_exception() { // here we test a case when we deploy a contract where constructor throws StackUnderflowException // 0x01 is the ADD opcode which requires two items on the stack and stack is empty here // added here for full coverage as the errors (EVM) are handled differently in some cases (via .NET exceptions) - var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _sender, 0, "0x01"); - delta.ContractEntries[0].GasLimit = 1_000_000L; + var delta = EntryUtils.PrepareSingleContractEntryDelta(null, _senderPublicKey, 0, "0x01"); + delta.PublicEntries[0].GasLimit = 1_000_000L; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - - var contractAddress = Address.OfContract(_sender.ToKvmAddress(), 0); - tracer.Received().MarkAsFailed(contractAddress, 1_000_000L, Arg.Any(), "Error"); + + var contractAddress = ContractAddress.From(_senderPublicKey.ToKvmAddress(), 0); + tracer.Received().MarkAsFailed(contractAddress, 1_000_000L, Arg.Any(), "StackUnderflow"); } - - [Fact] + + [Test] public void Can_execute_transfers_from_public_entries() { - var delta = EntryUtils.PrepareSinglePublicEntryDelta(_recipient, _sender, 0); + var delta = EntryUtils.PrepareSinglePublicEntryDelta(_recipient, _senderPublicKey, 0); + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - + tracer.Received().MarkAsSuccess(_recipient.ToKvmAddress(), 21000, Bytes.Empty, Arg.Any()); } - - [Fact] + + [Test] public void Can_add_gas_to_existing_balance() { - var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _sender, 0); - delta.ContractEntries[0].GasPrice = 1; + var delta = EntryUtils.PrepareSingleContractEntryDelta(_recipient, _senderPublicKey, 0); + delta.PublicEntries[0].GasPrice = 1.ToUint256ByteString(); + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); var tracer = Substitute.For(); tracer.IsTracingReceipt.Returns(true); - + _executor.Execute(delta, tracer); - + tracer.Received().MarkAsSuccess(_recipient.ToKvmAddress(), 21000, Bytes.Empty, Arg.Any()); } } diff --git a/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/CatalystVirtualMachineTests.cs b/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/CatalystVirtualMachineTests.cs index 5c80703ec8..7f0d374f7d 100644 --- a/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/CatalystVirtualMachineTests.cs +++ b/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/CatalystVirtualMachineTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,24 +21,30 @@ #endregion +using Catalyst.Core.Modules.Cryptography.BulletProofs; +using Catalyst.Core.Modules.Hashing; +using MultiFormats.Registry; using Nethermind.Evm; using Nethermind.Logging; -using Nethermind.Store; +using Nethermind.State; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Kvm.Tests.UnitTests { + [TestFixture] public sealed class CatalystVirtualMachineTests { - [Fact] + [Test] public void Catalyst_virtual_machine_can_be_initialized() { - KatVirtualMachine virtualMachine = new KatVirtualMachine( + var virtualMachine = new KatVirtualMachine( Substitute.For(), Substitute.For(), Substitute.For(), - new CatalystSpecProvider(), + new CatalystSpecProvider(), + new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")), + new FfiWrapper(), LimboLogs.Instance); Assert.NotNull(virtualMachine); } diff --git a/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/RangeProofPrecompileTests.cs b/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/RangeProofPrecompileTests.cs index 93ad4e0cf2..5bfffc54c5 100644 --- a/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/RangeProofPrecompileTests.cs +++ b/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/RangeProofPrecompileTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,27 +22,28 @@ #endregion using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Kvm.Tests.UnitTests { /// /// This is just a placeholder for the actual tests later. /// + [TestFixture] public sealed class RangeProofPrecompileTests { - [Fact] + [Test] public void Base_Gas_Cost_Should_Return_200000() { - RangeProofPrecompile precompile = new RangeProofPrecompile(); + var precompile = new RangeProofPrecompile(); var baseCost = precompile.BaseGasCost(CatalystGenesisSpec.Instance); baseCost.Should().Be(200000); } - - [Fact] + + [Test] public void Base_Data_Gas_Cost_Should_Return_0() { - RangeProofPrecompile precompile = new RangeProofPrecompile(); + var precompile = new RangeProofPrecompile(); var dataCost = precompile.DataGasCost(new byte[32], CatalystGenesisSpec.Instance); dataCost.Should().Be(0); } diff --git a/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/StateHashProviderTests.cs b/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/StateHashProviderTests.cs index 4472b19a78..e5715f707d 100644 --- a/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/StateHashProviderTests.cs +++ b/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/StateHashProviderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,18 +22,19 @@ #endregion using Nethermind.Evm; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Kvm.Tests.UnitTests { + [TestFixture] public sealed class StateHashProviderTests { - [Fact(Skip = "Not implemented yet")] + [Ignore("Not implemented yet")] public void Can_provide_a_latest_state_update_hash() { - StateUpdate stateUpdate = new StateUpdate(); - - StateUpdateHashProvider stateUpdateHashProvider = new StateUpdateHashProvider(); + var stateUpdate = new StateUpdate(); + + var stateUpdateHashProvider = new StateUpdateHashProvider(); stateUpdateHashProvider.GetHash(stateUpdate, 1); } } diff --git a/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/Validators/ContractValidatorReaderTests.cs b/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/Validators/ContractValidatorReaderTests.cs new file mode 100644 index 0000000000..7a330c4265 --- /dev/null +++ b/src/Catalyst.Core.Modules.Kvm.Tests/UnitTests/Validators/ContractValidatorReaderTests.cs @@ -0,0 +1,95 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Validators; +using Catalyst.Core.Modules.Kvm.Validators; +using FluentAssertions; +using NUnit.Framework; +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; +using System.Linq; +using System.IO; +using Catalyst.Abstractions.Contract; +using NSubstitute; + +namespace Catalyst.Core.Modules.Kvm.Tests.UnitTests.Validators +{ + [TestFixture] + public class ContractValidatorReaderTests + { + [Test] + public void Can_Parse_Validator_List() + { + var json = @"{ + ""validators"": { + ""multi"": { + ""0"": { + ""list"": [ ""0x1a2149b4df5cbac970bc38fecc5237800c688c8b"" ] + }, + ""1"": { + ""list"": [ ""0x1a2149b4df5cbac970bc38fecc5237800c688c8c"" ] + }, + ""2"": { + ""contract"": ""0x79dd7e4c1b9adb07f71b54dba2d54db2fa549de3"" + } + } + } + }"; + + //Convert json to memory stream, there is a extension in Catalyst.Core.Lib.Extensions but + //because of circular reference it will need to be refactored and Catalyst.Core.Lib.Extensions + //functionality should be in its own class lib. + var memoryStream = new MemoryStream(); + var streamWriter = new StreamWriter(memoryStream); + streamWriter.Write(json); + streamWriter.Flush(); + memoryStream.Position = 0; + + //Build config from json memory stream + var configurationBuilder = new ConfigurationBuilder(); + configurationBuilder.AddJsonStream(memoryStream); + var config = configurationBuilder.Build(); + + var validatorSets = new List(); + + var multi = config.GetSection("validators:multi"); + var validatorSetAtStartBlocks = multi.GetChildren(); + + var validatorReader = new ContractValidatorReader(Substitute.For()); + + foreach (var validatorSetAtStartBlock in validatorSetAtStartBlocks) + { + if (validatorSetAtStartBlock.Key == null) + { + continue; + } + + var startBlock = long.Parse(validatorSetAtStartBlock.Key); + var property = validatorSetAtStartBlock.GetChildren().FirstOrDefault(); + validatorReader.AddValidatorSet(validatorSets, startBlock, property); + } + + validatorSets.Count.Should().Be(1); + } + } +} diff --git a/src/Catalyst.Core.Modules.Kvm/AddressExtensions.cs b/src/Catalyst.Core.Modules.Kvm/AddressExtensions.cs new file mode 100644 index 0000000000..6a5c29dbcd --- /dev/null +++ b/src/Catalyst.Core.Modules.Kvm/AddressExtensions.cs @@ -0,0 +1,45 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Cryptography; +using Catalyst.Core.Lib.Extensions; +using Google.Protobuf; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; + +namespace Catalyst.Core.Modules.Kvm +{ + public static class ByteStringExtensions + { + public static Address ToAddress(this ByteString addressByteString) + { + if (addressByteString == null || addressByteString.IsEmpty) + { + return null; + } + + return new Address(addressByteString.ToByteArray()); + } + } +} diff --git a/src/Catalyst.Core.Modules.Kvm/Blake2bPrecompile.cs b/src/Catalyst.Core.Modules.Kvm/Blake2bPrecompile.cs new file mode 100644 index 0000000000..607af3c7db --- /dev/null +++ b/src/Catalyst.Core.Modules.Kvm/Blake2bPrecompile.cs @@ -0,0 +1,56 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Hashing; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Evm; +using Nethermind.Evm.Precompiles; + +namespace Catalyst.Core.Modules.Kvm +{ + public sealed class Blake2bPrecompiledContract : IPrecompiledContract + { + private readonly IHashProvider _hashProvider; + + public Blake2bPrecompiledContract(IHashProvider hashProvider) + { + _hashProvider = hashProvider ?? throw new ArgumentNullException(nameof(hashProvider)); + } + + public Address Address { get; } = Address.FromNumber(1 + KatVirtualMachine.CatalystPrecompilesAddressingSpace); + + public long BaseGasCost(IReleaseSpec releaseSpec) { return 20L; } + + public long DataGasCost(byte[] inputData, IReleaseSpec releaseSpec) { return 12L * EvmPooledMemory.Div32Ceiling((ulong) inputData.Length); } + + public (byte[], bool) Run(byte[] inputData) + { + return (_hashProvider.ComputeMultiHash(inputData).Digest, true); + } + } +} diff --git a/src/Catalyst.Core.Modules.Kvm/Catalyst.Core.Modules.Kvm.csproj b/src/Catalyst.Core.Modules.Kvm/Catalyst.Core.Modules.Kvm.csproj index 6f0f7b15b7..03617bcb58 100644 --- a/src/Catalyst.Core.Modules.Kvm/Catalyst.Core.Modules.Kvm.csproj +++ b/src/Catalyst.Core.Modules.Kvm/Catalyst.Core.Modules.Kvm.csproj @@ -1,22 +1,28 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Kvm - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Kvm.snk true + + 1701;1702;CS8002 + + + + - + diff --git a/src/Catalyst.Core.Modules.Kvm/CatalystGenesisSpec.cs b/src/Catalyst.Core.Modules.Kvm/CatalystGenesisSpec.cs index f96c07758e..9ff5d09c6b 100644 --- a/src/Catalyst.Core.Modules.Kvm/CatalystGenesisSpec.cs +++ b/src/Catalyst.Core.Modules.Kvm/CatalystGenesisSpec.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,8 +24,8 @@ using System.Threading; using Nethermind.Core; using Nethermind.Core.Specs; -using Nethermind.Core.Specs.Forks; using Nethermind.Dirichlet.Numerics; +using Nethermind.Specs.Forks; namespace Catalyst.Core.Modules.Kvm { @@ -47,7 +47,7 @@ private CatalystGenesisSpec() { } /// public long MaximumExtraDataSize { get; } = Istanbul.Instance.MaximumExtraDataSize; - + /// public long MaxCodeSize { get; } = Istanbul.Instance.MaxCodeSize; @@ -57,9 +57,6 @@ private CatalystGenesisSpec() { } /// public long GasLimitBoundDivisor { get; } = Istanbul.Instance.GasLimitBoundDivisor; - /// - public Address Registrar { get; } = Istanbul.Instance.Registrar; - /// public UInt256 BlockReward { get; } = Istanbul.Instance.BlockReward; diff --git a/src/Catalyst.Core.Modules.Kvm/CatalystSpecProvider.cs b/src/Catalyst.Core.Modules.Kvm/CatalystSpecProvider.cs index 148930e7ef..77af89a156 100644 --- a/src/Catalyst.Core.Modules.Kvm/CatalystSpecProvider.cs +++ b/src/Catalyst.Core.Modules.Kvm/CatalystSpecProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,8 +29,10 @@ namespace Catalyst.Core.Modules.Kvm public sealed class CatalystSpecProvider : ISpecProvider { public IReleaseSpec GenesisSpec => CatalystGenesisSpec.Instance; - public IReleaseSpec GetSpec(long blockNumber) => GenesisSpec; + public IReleaseSpec GetSpec(long blockNumber) { return GenesisSpec; } + public long? DaoBlockNumber => null; public int ChainId => NetworkTypes.Dev.Id; // @TODO should we not be using protocol.common.network? + public long[] TransitionBlocks { get; } = {0}; } } diff --git a/src/Catalyst.Core.Modules.Kvm/CatalystUnit.cs b/src/Catalyst.Core.Modules.Kvm/CatalystUnit.cs index e08390d0b2..bef6a397bc 100644 --- a/src/Catalyst.Core.Modules.Kvm/CatalystUnit.cs +++ b/src/Catalyst.Core.Modules.Kvm/CatalystUnit.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -42,7 +42,7 @@ public static class CatalystUnit { public static readonly UInt256 Mol = 1; public static readonly UInt256 Gmol = 1_000_000_000; - public static readonly UInt256 Katal = 1_000_000_000_000; + public static readonly UInt256 Katal = 1_000_000_000_000_000_000; public static readonly string KatalSymbol = "KAT"; } } diff --git a/src/Catalyst.Core.Modules.Kvm/DeltaExecutor.cs b/src/Catalyst.Core.Modules.Kvm/DeltaExecutor.cs index fcea067819..ed360fe265 100644 --- a/src/Catalyst.Core.Modules.Kvm/DeltaExecutor.cs +++ b/src/Catalyst.Core.Modules.Kvm/DeltaExecutor.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,6 +22,7 @@ #endregion using System; +using System.IO; using System.Linq; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.Kvm; @@ -30,6 +31,7 @@ using Catalyst.Protocol.Transaction; using Google.Protobuf; using Nethermind.Core; +using Nethermind.Core.Attributes; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; @@ -37,7 +39,7 @@ using Nethermind.Evm; using Nethermind.Evm.Precompiles; using Nethermind.Evm.Tracing; -using Nethermind.Store; +using Nethermind.State; using Serilog; using Serilog.Events; @@ -85,9 +87,10 @@ public DeltaExecutor(ISpecProvider specProvider, } [Todo("Wider work needed to split calls and execution properly")] - public void CallAndRestore(Delta stateUpdate, ITxTracer txTracer) { Execute(stateUpdate, txTracer, true); } + public void CallAndReset(Delta stateUpdate, ITxTracer txTracer) { Execute(stateUpdate, txTracer, true); } - [Todo("After delta is executed we should validate the state root and if it is not as expected we should revert all the changes.")] + [Todo( + "After delta is executed we should validate the state root and if it is not as expected we should revert all the changes.")] public void Execute(Delta stateUpdate, ITxTracer txTracer) { Execute(stateUpdate, txTracer, false); } /// @@ -99,16 +102,18 @@ public DeltaExecutor(ISpecProvider specProvider, /// /// /// Total intrinsic cost of the - public static ulong CalculateIntrinsicGas(ContractEntry entry, IReleaseSpec releaseSpec) + public static ulong CalculateIntrinsicGas(PublicEntry entry, IReleaseSpec releaseSpec) { ulong result = GasCostOf.Transaction; // the basic entry cost if (entry.Data != null) { // here is the difference between the 0 bytes and non-zero bytes cost // justified by a better compression level of zero bytes - long txDataNonZeroGasCost = releaseSpec.IsEip2028Enabled ? GasCostOf.TxDataNonZeroEip2028 : GasCostOf.TxDataNonZero; - int length = entry.Data.Length; - for (int i = 0; i < length; i++) + var txDataNonZeroGasCost = releaseSpec.IsEip2028Enabled + ? GasCostOf.TxDataNonZeroEip2028 + : GasCostOf.TxDataNonZero; + var length = entry.Data.Length; + for (var i = 0; i < length; i++) { result += entry.Data[i] == 0 ? GasCostOf.TxDataZero : (ulong) txDataNonZeroGasCost; } @@ -125,7 +130,7 @@ public static ulong CalculateIntrinsicGas(ContractEntry entry, IReleaseSpec rele [Todo(Improve.MissingFunctionality, "We need to agree on delta to state mapping details")] private StateUpdate ToStateUpdate(Delta delta) { - StateUpdate result = new StateUpdate + var result = new StateUpdate { Difficulty = 1, Number = 1, @@ -140,18 +145,7 @@ private StateUpdate ToStateUpdate(Delta delta) return result; } - private Address GetAccountAddress(ByteString publicKeyByteString) - { - if (publicKeyByteString == null || publicKeyByteString.IsEmpty) - { - return null; - } - - IPublicKey publicKey = _cryptoContext.GetPublicKeyFromBytes(publicKeyByteString.ToByteArray()); - return publicKey.ToKvmAddress(); - } - - private static void QuickFail(ContractEntry entry, ExecutionEnvironment env, ITxTracer txTracer) + private static void QuickFail(PublicEntry entry, ExecutionEnvironment env, ITxTracer txTracer) { // here we need to propagate back to Delta env.CurrentBlock.GasUsed += (long) entry.GasLimit; @@ -164,31 +158,36 @@ private static void QuickFail(ContractEntry entry, ExecutionEnvironment env, ITx private void Execute(Delta delta, ITxTracer txTracer, bool readOnly) { var stateUpdate = ToStateUpdate(delta); - foreach (var publicEntry in delta.PublicEntries) - { - var contractEntry = new ContractEntry - { - Base = publicEntry.Base, Amount = publicEntry.Amount, Data = ByteString.Empty, GasLimit = 21000 - }; - Execute(contractEntry, stateUpdate, txTracer, readOnly); - } // revert state if any fails (take snapshot) - foreach (var contractEntry in delta.ContractEntries) + foreach (var publicEntry in delta.PublicEntries) { - Execute(contractEntry, stateUpdate, txTracer, readOnly); + Execute(publicEntry, stateUpdate, txTracer); } - + + var spec = _specProvider.GetSpec(stateUpdate.Number); + _storageProvider.Commit(txTracer.IsTracingState ? txTracer : null); + _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : null); + + _stateProvider.RecalculateStateRoot(); if (!readOnly) { - // we should assign block rewards here (or in Ledger) - _stateProvider.CommitTree(); + if (new Keccak(delta.StateRoot.ToByteArray()) != _stateProvider.StateRoot) + { + if (_logger.IsEnabled(LogEventLevel.Error)) _logger.Error("Invalid delta state root - found {found} and should be {shouldBe}", _stateProvider.StateRoot, new Keccak(delta.StateRoot.ToByteArray())); + } + + // compare state roots _storageProvider.CommitTrees(); + _stateProvider.CommitTree(); } else { - _storageProvider.Reset(); + + delta.StateRoot = _stateProvider.StateRoot.ToByteString(); + if (_logger.IsEnabled(LogEventLevel.Debug)) _logger.Debug($"Setting candidate delta {delta.DeltaNumber} root to {delta.StateRoot.ToKeccak()}"); _stateProvider.Reset(); + _storageProvider.Reset(); } } @@ -197,7 +196,7 @@ private void Execute(Delta delta, ITxTracer txTracer, bool readOnly) /// /// /// - private void TraceLogInvalidTx(ContractEntry entry, string reason) + private void TraceLogInvalidTx(PublicEntry entry, string reason) { if (_logger.IsEnabled(LogEventLevel.Verbose)) { @@ -206,20 +205,21 @@ private void TraceLogInvalidTx(ContractEntry entry, string reason) } /// - /// Executes the . + /// Executes the . /// - /// Transaction entry to be executed inside the . - /// to be used for execution environment construction + /// Transaction entry to be executed inside the . + /// to be used for execution environment construction /// Tracer to extract the execution steps for debugging or analytics. /// Defines whether the state should be reverted after the execution. /// Thrown when deployment address already has some code. /// Thrown when not enough gas is available for deposit. - private void Execute(ContractEntry entry, StateUpdate stateUpdate, ITxTracer txTracer, bool readOnly) + private void Execute(PublicEntry entry, StateUpdate stateUpdate, ITxTracer txTracer) { var spec = _specProvider.GetSpec(stateUpdate.Number); var (sender, recipient) = ExtractSenderAndRecipient(entry); var isPrecompile = recipient.IsPrecompiled(spec); + var env = PrepareEnv(entry, sender, recipient, stateUpdate, isPrecompile); var gasLimit = entry.GasLimit; @@ -283,13 +283,10 @@ private void Execute(ContractEntry entry, StateUpdate stateUpdate, ITxTracer txT try { - if (entry.IsValidDeploymentEntry) - { - PrepareContractAccount(env.CodeSource); - } - + if (entry.IsValidDeploymentEntry) PrepareContractAccount(env.CodeSource); + var executionType = entry.IsValidDeploymentEntry ? ExecutionType.Create : ExecutionType.Call; - using (var state = new VmState((long) unspentGas, env, executionType, isPrecompile, true, false)) + using (VmState state = new((long) unspentGas, env, executionType, isPrecompile, true, false)) { substate = _virtualMachine.Run(state, txTracer); unspentGas = (ulong) state.GasAvailable; @@ -301,7 +298,7 @@ private void Execute(ContractEntry entry, StateUpdate stateUpdate, ITxTracer txT { _logger.Verbose("Restoring state from before transaction"); } - + _stateProvider.Restore(stateSnapshot); _storageProvider.Restore(storageSnapshot); } @@ -311,7 +308,7 @@ private void Execute(ContractEntry entry, StateUpdate stateUpdate, ITxTracer txT { DeployCode(env, substate, ref unspentGas, spec); } - + DestroyAccounts(substate); statusCode = StatusCode.Success; } @@ -324,7 +321,7 @@ private void Execute(ContractEntry entry, StateUpdate stateUpdate, ITxTracer txT { _logger.Verbose($"EVM EXCEPTION: {ex.GetType().Name}"); } - + _stateProvider.Restore(stateSnapshot); _storageProvider.Restore(storageSnapshot); } @@ -349,23 +346,12 @@ private void Execute(ContractEntry entry, StateUpdate stateUpdate, ITxTracer txT } } - if (!readOnly) - { - _storageProvider.Commit(txTracer.IsTracingState ? txTracer : null); - _stateProvider.Commit(spec, txTracer.IsTracingState ? txTracer : null); - stateUpdate.GasUsed += (long) spentGas; - } - else - { - _storageProvider.Reset(); - _stateProvider.Reset(); - } - if (txTracer.IsTracingReceipt) { if (statusCode == StatusCode.Failure) { - txTracer.MarkAsFailed(env.CodeSource, (long) spentGas, substate?.ShouldRevert ?? false ? substate.Output : Bytes.Empty, substate?.Error); + txTracer.MarkAsFailed(env.CodeSource, (long) spentGas, + substate?.ShouldRevert ?? false ? substate.Output : Bytes.Empty, substate?.Error); } else { @@ -373,15 +359,16 @@ private void Execute(ContractEntry entry, StateUpdate stateUpdate, ITxTracer txT { throw new InvalidOperationException("Substate should not be null after a successful VM run."); } - - txTracer.MarkAsSuccess(env.CodeSource, (long) spentGas, substate.Output, substate.Logs.Any() ? substate.Logs.ToArray() : LogEntry.EmptyLogs); + + txTracer.MarkAsSuccess(env.CodeSource, (long) spentGas, substate.Output, + substate.Logs.Any() ? substate.Logs.ToArray() : LogEntry.EmptyLogs); } } } /// - /// Accounts for which SELF_DESTRUCT opcode was invoked during the tx execution. - /// Note that one of them can be coinbase / validator and in such case the miner / validator rewards are lost. + /// Accounts for which SELF_DESTRUCT opcode was invoked during the tx execution. + /// Note that one of them can be coinbase / validator and in such case the miner / validator rewards are lost. /// /// private void DestroyAccounts(TransactionSubstate substate) @@ -392,12 +379,15 @@ private void DestroyAccounts(TransactionSubstate substate) { _logger.Verbose($"Destroying account {toBeDestroyed}"); } - + _stateProvider.DeleteAccount(toBeDestroyed); } } - - private void DeployCode(ExecutionEnvironment env, TransactionSubstate substate, ref ulong unspentGas, IReleaseSpec spec) + + private void DeployCode(ExecutionEnvironment env, + TransactionSubstate substate, + ref ulong unspentGas, + IReleaseSpec spec) { var codeDepositGasCost = (ulong) CodeDepositHandler.CalculateCost(substate.Output.Length, spec); if (unspentGas < codeDepositGasCost && spec.IsEip2Enabled) @@ -409,27 +399,25 @@ private void DeployCode(ExecutionEnvironment env, TransactionSubstate substate, { return; } - + var codeHash = _stateProvider.UpdateCode(substate.Output); _stateProvider.UpdateCodeHash(env.CodeSource, codeHash, spec); unspentGas -= codeDepositGasCost; } - private (Address sender, Address recipient) ExtractSenderAndRecipient(ContractEntry entry) + private (Address sender, Address recipient) ExtractSenderAndRecipient(PublicEntry entry) { - var sender = GetAccountAddress(entry.Base.SenderPublicKey); - var recipient = entry.TargetContract == null - ? GetAccountAddress(entry.Base.ReceiverPublicKey) - : new Address(entry.TargetContract); + var sender = entry.SenderAddress.ToAddress(); + var recipient = entry.ReceiverAddress.ToAddress(); if (entry.IsValidDeploymentEntry) { - recipient = Address.OfContract(sender, _stateProvider.GetNonce(sender)); + recipient = ContractAddress.From(sender, _stateProvider.GetNonce(sender)); } return (sender, recipient); } - private ExecutionEnvironment PrepareEnv(ContractEntry entry, + private ExecutionEnvironment PrepareEnv(PublicEntry entry, Address sender, Address recipient, StateUpdate stateUpdate, @@ -447,7 +435,7 @@ private ExecutionEnvironment PrepareEnv(ContractEntry entry, CodeSource = recipient, ExecutingAccount = recipient, CurrentBlock = stateUpdate, - GasPrice = entry.GasPrice, + GasPrice = entry.GasPrice.ToUInt256(), InputData = data ?? new byte[0], CodeInfo = isPrecompile ? new CodeInfo(recipient) @@ -459,19 +447,20 @@ private ExecutionEnvironment PrepareEnv(ContractEntry entry, return env; } - private bool ValidateDeltaGasLimit(ContractEntry entry, ExecutionEnvironment env, ITxTracer txTracer) + private bool ValidateDeltaGasLimit(PublicEntry entry, ExecutionEnvironment env, ITxTracer txTracer) { if (entry.GasLimit <= (ulong) (env.CurrentBlock.GasLimit - env.CurrentBlock.GasUsed)) { return true; } - - TraceLogInvalidTx(entry, $"BLOCK_GAS_LIMIT_EXCEEDED {entry.GasLimit.ToString()} > {env.CurrentBlock.GasLimit.ToString()} - {env.CurrentBlock.GasUsed.ToString()}"); + + TraceLogInvalidTx(entry, + $"BLOCK_GAS_LIMIT_EXCEEDED {entry.GasLimit.ToString()} > {env.CurrentBlock.GasLimit.ToString()} - {env.CurrentBlock.GasUsed.ToString()}"); QuickFail(entry, env, txTracer); return false; } - private bool ValidateIntrinsicGas(ContractEntry entry, + private bool ValidateIntrinsicGas(PublicEntry entry, ExecutionEnvironment env, ulong intrinsicGas, ITxTracer txTracer) @@ -485,37 +474,39 @@ private bool ValidateIntrinsicGas(ContractEntry entry, { return true; } - - TraceLogInvalidTx(entry, $"GAS_LIMIT_BELOW_INTRINSIC_GAS {entry.GasLimit.ToString()} < {intrinsicGas.ToString()}"); + + TraceLogInvalidTx(entry, + $"GAS_LIMIT_BELOW_INTRINSIC_GAS {entry.GasLimit.ToString()} < {intrinsicGas.ToString()}"); QuickFail(entry, env, txTracer); return false; } - private bool ValidateSender(ContractEntry entry, ExecutionEnvironment env, ITxTracer txTracer) + private bool ValidateSender(PublicEntry entry, ExecutionEnvironment env, ITxTracer txTracer) { if (env.Sender != null) { return true; } - + TraceLogInvalidTx(entry, "SENDER_NOT_SPECIFIED"); QuickFail(entry, env, txTracer); return false; } - private bool ValidateNonce(ContractEntry entry, ExecutionEnvironment env, ITxTracer txTracer) + private bool ValidateNonce(PublicEntry entry, ExecutionEnvironment env, ITxTracer txTracer) { - if (entry.Base.Nonce == _stateProvider.GetNonce(env.Sender)) + if (entry.Nonce == _stateProvider.GetNonce(env.Sender)) { return true; } - - TraceLogInvalidTx(entry, $"WRONG_TRANSACTION_NONCE: {entry.Base.Nonce.ToString()} (expected {_stateProvider.GetNonce(env.Sender).ToString()})"); + + TraceLogInvalidTx(entry, + $"WRONG_TRANSACTION_NONCE: {entry.Nonce.ToString()} (expected {_stateProvider.GetNonce(env.Sender).ToString()})"); QuickFail(entry, env, txTracer); return false; } - private bool ValidateSenderBalance(ContractEntry entry, + private bool ValidateSenderBalance(PublicEntry entry, ExecutionEnvironment env, ulong intrinsicGas, ITxTracer txTracer) @@ -525,24 +516,22 @@ private bool ValidateSenderBalance(ContractEntry entry, { return true; } - - TraceLogInvalidTx(entry, $"INSUFFICIENT_SENDER_BALANCE: ({env.Sender})_BALANCE = {senderBalance.ToString()}"); + + TraceLogInvalidTx(entry, + $"INSUFFICIENT_SENDER_BALANCE: ({env.Sender})_BALANCE = {senderBalance.ToString()}"); QuickFail(entry, env, txTracer); return false; } /// - /// Prepares and validates the account address for contract deployment. + /// Prepares and validates the account address for contract deployment. /// /// Contract address. /// Thrown when the address is already in use private void PrepareContractAccount(Address recipient) { - if (!_stateProvider.AccountExists(recipient)) - { - return; - } - + if (!_stateProvider.AccountExists(recipient)) return; + var addressHasCode = (_virtualMachine.GetCachedCodeInfo(recipient)?.MachineCode?.Length ?? 0) != 0; var addressWasUsed = _stateProvider.GetNonce(recipient) != 0; if (addressHasCode || addressWasUsed) @@ -568,17 +557,22 @@ private void InitEntryExecution(ExecutionEnvironment env, ulong gasLimit, IRelea _stateProvider.SubtractFromBalance(env.Sender, gasLimit * env.GasPrice, spec); // we commit the nonce and gas payment - _stateProvider.Commit(_specProvider.GetSpec(env.CurrentBlock.Number), txTracer.IsTracingState ? txTracer : null); + _stateProvider.Commit(_specProvider.GetSpec(env.CurrentBlock.Number), + txTracer.IsTracingState ? txTracer : null); } /// - /// Refunds are issued as a result of calling specific opcodes like SSTORE or - /// SELFDESTRUCT. + /// Refunds are issued as a result of calling specific opcodes like SSTORE or + /// SELFDESTRUCT. /// - /// Gas limit of the entry needed here because - /// ( - ) / 2 is a hard cap for the gas refund. - /// Unspent gas of the entry needed here because - /// ( - ) / 2 is a hard cap for the gas refund. + /// + /// Gas limit of the entry needed here because + /// ( - ) / 2 is a hard cap for the gas refund. + /// + /// + /// Unspent gas of the entry needed here because + /// ( - ) / 2 is a hard cap for the gas refund. + /// /// Substate of the transaction before the refunds are issued. /// Details of the execution environment. /// Provides the refund logic version details. @@ -594,7 +588,7 @@ private ulong Refund(ulong gasLimit, { return spentGas; } - + spentGas -= unspentGas; var refund = substate.ShouldRevert ? 0 diff --git a/src/Catalyst.Core.Modules.Kvm/Ed25519VerifyPrecompile.cs b/src/Catalyst.Core.Modules.Kvm/Ed25519VerifyPrecompile.cs new file mode 100644 index 0000000000..c44933d070 --- /dev/null +++ b/src/Catalyst.Core.Modules.Kvm/Ed25519VerifyPrecompile.cs @@ -0,0 +1,63 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.Cryptography; +using Nethermind.Core; +using Nethermind.Core.Extensions; +using Nethermind.Core.Specs; +using Nethermind.Evm.Precompiles; + +namespace Catalyst.Core.Modules.Kvm +{ + public sealed class Ed25519VerifyPrecompile : IPrecompiledContract + { + private readonly ICryptoContext _cryptoContext; + + public Ed25519VerifyPrecompile(ICryptoContext cryptoContext) { _cryptoContext = cryptoContext ?? throw new ArgumentNullException(nameof(cryptoContext)); } + + public Address Address { get; } = Address.FromNumber(2 + KatVirtualMachine.CatalystPrecompilesAddressingSpace); + + public long DataGasCost(byte[] inputData, IReleaseSpec releaseSpec) { return 0L; } + + public long BaseGasCost(IReleaseSpec releaseSpec) { return 3000L; } + + public (byte[], bool) Run(byte[] inputData) + { + if (inputData.Length != 160) + { + return (Bytes.Empty, false); + } + + byte[] message = inputData.AsSpan().Slice(0, 32).ToArray(); + byte[] signatureBytes = inputData.AsSpan().Slice(32, 64).ToArray(); + byte[] signingContext = inputData.AsSpan().Slice(96, 32).ToArray(); + byte[] publicKey = inputData.AsSpan().Slice(128, 32).ToArray(); + + ISignature signature = _cryptoContext.GetSignatureFromBytes(signatureBytes, publicKey); + return _cryptoContext.Verify(signature, message, signingContext) + ? (new byte[] {1}, true) + : (new byte[] {0}, true); + } + } +} diff --git a/src/Catalyst.Core.Modules.Kvm/KatVirtualMachine.cs b/src/Catalyst.Core.Modules.Kvm/KatVirtualMachine.cs index 7440f0add3..ed28860075 100644 --- a/src/Catalyst.Core.Modules.Kvm/KatVirtualMachine.cs +++ b/src/Catalyst.Core.Modules.Kvm/KatVirtualMachine.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,38 +21,68 @@ #endregion +using System; using System.Numerics; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.Kvm; +using Catalyst.Core.Modules.Hashing; +using MultiFormats.Registry; using Nethermind.Core; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; using Nethermind.Evm; using Nethermind.Logging; -using Nethermind.Store; +using Nethermind.State; namespace Catalyst.Core.Modules.Kvm { public sealed class KatVirtualMachine : VirtualMachine, IKvm { - public KatVirtualMachine(IStateProvider stateProvider, IStorageProvider storageProvider, IStateUpdateHashProvider blockhashProvider, ISpecProvider specProvider, ILogManager logManager) - : base(stateProvider, storageProvider, blockhashProvider, specProvider, logManager) { } + private readonly IHashProvider _hashProvider; + private readonly ICryptoContext _cryptoContext; + public const int CatalystPrecompilesAddressingSpace = 0xffff; - protected override void InitializePrecompiledContracts() + public KatVirtualMachine(IStateProvider stateProvider, + IStorageProvider storageProvider, + IStateUpdateHashProvider blockhashProvider, + ISpecProvider specProvider, + IHashProvider hashProvider, + ICryptoContext cryptoContext, + ILogManager logManager) + : base(stateProvider, storageProvider, blockhashProvider, specProvider, logManager) { - base.InitializePrecompiledContracts(); - Precompiles[RangeProofPrecompile.AddressInKvm] = new RangeProofPrecompile(); + _hashProvider = hashProvider ?? throw new ArgumentNullException(nameof(hashProvider)); + _cryptoContext = cryptoContext ?? throw new ArgumentNullException(nameof(cryptoContext)); + + AddCatalystPrecompiledContracts(); + } + + private void AddCatalystPrecompiledContracts() + { + Blake2bPrecompiledContract blake2BPrecompiledContract = new(new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256"))); + Precompiles[blake2BPrecompiledContract.Address] = blake2BPrecompiledContract; + + Ed25519VerifyPrecompile ed25519VerifyPrecompile = new(_cryptoContext); + Precompiles[ed25519VerifyPrecompile.Address] = ed25519VerifyPrecompile; } - private static readonly BigInteger RangeProofAddressAsInt = RangeProofPrecompile.AddressInKvm.Bytes.ToUnsignedBigInteger(); - - /// - /// This will probably be removed - /// protected override bool IsPrecompiled(Address address, IReleaseSpec releaseSpec) { - // this will be optimized + return base.IsPrecompiled(address, releaseSpec) || IsCatalystPrecompiled(address); + } + + private static bool IsCatalystPrecompiled(Address address) + { + if (address[0] != 0) + { + return false; + } + BigInteger asInt = address.Bytes.ToUnsignedBigInteger(); - return base.IsPrecompiled(address, releaseSpec) || asInt == RangeProofAddressAsInt; + return + asInt > CatalystPrecompilesAddressingSpace + && asInt <= CatalystPrecompilesAddressingSpace + 2; } } } diff --git a/src/Catalyst.Core.Modules.Kvm/KvmIntExtensions.cs b/src/Catalyst.Core.Modules.Kvm/KvmIntExtensions.cs index d0b8b48782..3d2f2c0bec 100644 --- a/src/Catalyst.Core.Modules.Kvm/KvmIntExtensions.cs +++ b/src/Catalyst.Core.Modules.Kvm/KvmIntExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -40,4 +40,3 @@ public static class KvmIntExtensions public static UInt256 GFul(this int @this) { return (uint) @this * CatalystUnit.Gmol; } } } - diff --git a/src/Catalyst.Core.Modules.Kvm/KvmModule.cs b/src/Catalyst.Core.Modules.Kvm/KvmModule.cs index d7627936d8..1fedb9ad71 100644 --- a/src/Catalyst.Core.Modules.Kvm/KvmModule.cs +++ b/src/Catalyst.Core.Modules.Kvm/KvmModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,32 +21,132 @@ #endregion +using System; using Autofac; +using Autofac.Builder; +using Autofac.Core; +using Catalyst.Abstractions.Contract; using Catalyst.Abstractions.Kvm; +using Catalyst.Abstractions.Validators; +using Catalyst.Core.Lib.FileSystem; +using Catalyst.Core.Modules.Kvm.Validators; +using Catalyst.Module.ConvanSmartContract.Contract; +using Nethermind.Abi; using Nethermind.Core.Specs; +using Nethermind.Db; +using Nethermind.Db.Rocks; +using Nethermind.Db.Rocks.Config; using Nethermind.Evm; using Nethermind.Logging; -using Nethermind.Store; +using Nethermind.State; namespace Catalyst.Core.Modules.Kvm { - public class KvmModule : Module + public class KvmModule : Autofac.Module { + private readonly bool _useInMemoryDb; + public KvmModule() : this(false) { } + + public KvmModule(bool useInMemoryDb) + { + _useInMemoryDb = useInMemoryDb; + } + protected override void Load(ContainerBuilder builder) { - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); builder.RegisterType().As(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); + + // builder.RegisterInstance(new OneLoggerLogManager(new SimpleConsoleLogger())).As(); builder.RegisterInstance(LimboLogs.Instance).As(); - - builder.RegisterInstance(new MemDb()).As(); // code db - builder.RegisterInstance(new StateDb()).As(); // state db - - builder.RegisterType().As(); // state db - } + + var catDir = new FileSystem().GetCatalystDataDir().FullName; + var codeDb = _useInMemoryDb ? new MemDb() : (IDb)new CodeRocksDb(catDir, DbConfig.Default); + StateDb code = new(codeDb); + var stateDb = _useInMemoryDb ? new MemDb() : (IDb)new StateRocksDb(catDir, DbConfig.Default); + StateDb state = new(stateDb); + + builder.RegisterInstance(code).As().Named("codeDb").SingleInstance(); + builder.RegisterInstance(state).As().Named("stateDb").SingleInstance(); + builder.RegisterInstance(code).As().Named("codeDb").SingleInstance(); + builder.RegisterInstance(state).As().Named("stateDb").SingleInstance(); + + //builder.RegisterInstance(new MemDb()).As().SingleInstance(); // code db + //builder.RegisterInstance(new StateDb()).As().SingleInstance(); // state db + + builder.RegisterType().As().WithStateDbParameters(builder); + + builder.RegisterType().As().SingleInstance(); + + builder.RegisterType().As().SingleInstance().WithExecutionParameters(builder); + + builder.RegisterType().As().SingleInstance().WithExecutionParameters(builder); + } + } + + public static class ExecutionRegistrations + { + /// + /// Registers a custom set of the following components: , , and for the given . + /// + /// The registration to be enhanced. + /// The container builder. + /// The . + public static IRegistrationBuilder WithExecutionParameters(this IRegistrationBuilder registration, ContainerBuilder builder) + where TReflectionActivatorData : ReflectionActivatorData + { + var serviceName = Guid.NewGuid().ToString(); + + ByTypeNamedParameter stateProvider = new(serviceName); + ByTypeNamedParameter storageProvider = new(serviceName); + ByTypeNamedParameter kvm = new(serviceName); + ByTypeNamedParameter executor = new(serviceName); + + builder.RegisterType().Named(serviceName).SingleInstance() + .WithStateDbParameters(builder); + + builder.RegisterType().Named(serviceName).SingleInstance() + .WithParameter(stateProvider) + .WithStateDbParameters(builder); + + builder.RegisterType().Named(serviceName).SingleInstance() + .WithParameter(stateProvider) + .WithParameter(storageProvider); + builder.RegisterType().Named(serviceName).SingleInstance() + .WithParameter(stateProvider) + .WithParameter(storageProvider) + .WithParameter(kvm); + + // parameter registration + registration + .WithParameter(stateProvider) + .WithParameter(storageProvider) + .WithParameter(kvm) + .WithParameter(executor); + + return registration; + } + + public static IRegistrationBuilder WithStateDbParameters(this IRegistrationBuilder registration, ContainerBuilder builder) + where TReflectionActivatorData : ReflectionActivatorData + { + registration + .WithParameter(new ResolvedParameter((p, ctx) => p.Name == "codeDb", (p, ctx) => ctx.ResolveNamed("codeDb"))) + .WithParameter(new ResolvedParameter((p, ctx) => p.Name == "codeDb", (p, ctx) => ctx.ResolveNamed("codeDb"))) + .WithParameter(new ResolvedParameter((p, ctx) => p.Name == "stateDb", (p, ctx) => ctx.ResolveNamed("stateDb"))) + .WithParameter(new ResolvedParameter((p, ctx) => p.Name == "stateDb", (p, ctx) => ctx.ResolveNamed("stateDb"))); + + return registration; + } + + /// + /// Resolves a parameter of specific type with its named service. + /// + /// + sealed class ByTypeNamedParameter : ResolvedParameter + { + public ByTypeNamedParameter(string name) : base((p, _) => p.ParameterType == typeof(T), (_, c) => c.ResolveNamed(name)) { } + } } } diff --git a/src/Catalyst.Core.Modules.Kvm/PublicKeyExtensions.cs b/src/Catalyst.Core.Modules.Kvm/PublicKeyExtensions.cs index 495f1fb2ac..8671b17d61 100644 --- a/src/Catalyst.Core.Modules.Kvm/PublicKeyExtensions.cs +++ b/src/Catalyst.Core.Modules.Kvm/PublicKeyExtensions.cs @@ -1,7 +1,7 @@ -#region LICENSE +#region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,15 +22,40 @@ #endregion using Catalyst.Abstractions.Cryptography; +using Catalyst.Core.Lib.Extensions; +using Google.Protobuf; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; +using Nethermind.Evm; namespace Catalyst.Core.Modules.Kvm { public static class PublicKeyExtensions { - public static Address ToKvmAddress(this IPublicKey publicKey) => - new Address(ValueKeccak.Compute(publicKey.Bytes).BytesAsSpan.SliceWithZeroPadding(0, 20).ToArray()); + public static Address ToKvmAddress(this IPublicKey publicKey) + { + if (publicKey == null) + { + return null; + } + + return ToKvmAddress(publicKey.Bytes); + } + + public static Address ToKvmAddress(this byte[] publicKey) + { + return new Address(ValueKeccak.Compute(publicKey).BytesAsSpan.SliceWithZeroPadding(0, 20).ToArray()); + } + + public static ByteString ToKvmAddressByteString(this IPublicKey recipient) + { + return recipient?.ToKvmAddress().Bytes.ToByteString() ?? ByteString.Empty; + } + + public static ByteString ToKvmAddressByteString(this byte[] publicKey) + { + return publicKey?.ToKvmAddress().Bytes.ToByteString() ?? ByteString.Empty; + } } } diff --git a/src/Catalyst.Core.Modules.Kvm/RangeProofPrecompile.cs b/src/Catalyst.Core.Modules.Kvm/RangeProofPrecompile.cs index 002523b72c..005dc4e530 100644 --- a/src/Catalyst.Core.Modules.Kvm/RangeProofPrecompile.cs +++ b/src/Catalyst.Core.Modules.Kvm/RangeProofPrecompile.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,22 +30,23 @@ namespace Catalyst.Core.Modules.Kvm /// public sealed class RangeProofPrecompile : IPrecompiledContract { - private const int EthereumPrecompilesAddressingSpace = 0xffff; - /// /// /// https: //github.com/ethereum/EIPs/blob/master/EIPS/eip-1352.md /// 65535 (0xffff) will be registered for Ethereum, so we can start after that /// public Address Address => AddressInKvm; - - public static Address AddressInKvm { get; } = Address.FromNumber(1 + EthereumPrecompilesAddressingSpace); + + public static Address AddressInKvm { get; } = Address.FromNumber(3 + KatVirtualMachine.CatalystPrecompilesAddressingSpace); /// public long BaseGasCost(IReleaseSpec releaseSpec) { return 200000; } // numbers need to be benchmarked /// - public long DataGasCost(byte[] inputData, IReleaseSpec releaseSpec) { return 0; } // numbers need to be benchmarked + public long DataGasCost(byte[] inputData, IReleaseSpec releaseSpec) + { + return 0; + } // numbers need to be benchmarked /// public (byte[], bool) Run(byte[] inputData) { return (new byte[32], true); } diff --git a/src/Catalyst.Core.Modules.Kvm/StateUpdateHashProvider.cs b/src/Catalyst.Core.Modules.Kvm/StateUpdateHashProvider.cs index 55b24f4599..f03328f088 100644 --- a/src/Catalyst.Core.Modules.Kvm/StateUpdateHashProvider.cs +++ b/src/Catalyst.Core.Modules.Kvm/StateUpdateHashProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Kvm/Validators/ContractValidatorReader.cs b/src/Catalyst.Core.Modules.Kvm/Validators/ContractValidatorReader.cs new file mode 100644 index 0000000000..a5a8ddfcf1 --- /dev/null +++ b/src/Catalyst.Core.Modules.Kvm/Validators/ContractValidatorReader.cs @@ -0,0 +1,49 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Contract; +using Catalyst.Abstractions.Validators; +using Microsoft.Extensions.Configuration; +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Kvm.Validators +{ + public class ContractValidatorReader : IValidatorReader + { + private readonly IValidatorSetContract _validatorSetContract; + + public ContractValidatorReader(IValidatorSetContract validatorSetContract) + { + _validatorSetContract = validatorSetContract; + } + + public void AddValidatorSet(IList validatorSets, long startBlock, IConfigurationSection configurationSection) + { + if (configurationSection.Key.ToLower() == "contract") + { + var contractAddress = configurationSection.Value; + validatorSets.Add(new ContractValidatorSet(_validatorSetContract, startBlock, contractAddress)); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Kvm/Validators/ContractValidatorSet.cs b/src/Catalyst.Core.Modules.Kvm/Validators/ContractValidatorSet.cs new file mode 100644 index 0000000000..a3c4909e57 --- /dev/null +++ b/src/Catalyst.Core.Modules.Kvm/Validators/ContractValidatorSet.cs @@ -0,0 +1,47 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Contract; +using Catalyst.Abstractions.Validators; +using Catalyst.Module.ConvanSmartContract; +using Nethermind.Core; +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Kvm.Validators +{ + public class ContractValidatorSet : IValidatorSet + { + private readonly ValidatorSet _validatorSet; + + public long StartBlock { get; } + + public ContractValidatorSet(IValidatorSetContract validatorSetContract, long startBlock, string contractAddress) + { + StartBlock = startBlock; + _validatorSet = new ValidatorSet(validatorSetContract, new Address(contractAddress)); + } + + //Get validators from smart contract + public IEnumerable
GetValidators() => _validatorSet.GetValidators(); + } +} diff --git a/src/Catalyst.Core.Modules.Kvm/erc20/test/e_i_p20.js b/src/Catalyst.Core.Modules.Kvm/erc20/test/e_i_p20.js index 1d737b3019..067fe2a5b0 100644 --- a/src/Catalyst.Core.Modules.Kvm/erc20/test/e_i_p20.js +++ b/src/Catalyst.Core.Modules.Kvm/erc20/test/e_i_p20.js @@ -13,7 +13,7 @@ contract("EIP20", function(accounts) { token = instance; return token.balanceOf.call(accounts[0]); }).then(function(result){ - assert.equal(result.toNumber(), 1000000, 'balance is wrong'); + Assert.AreEqual(result.toNumber(), 1000000, 'balance is wrong'); }) }); @@ -25,10 +25,10 @@ contract("EIP20", function(accounts) { }).then(function(){ return token.balanceOf.call(accounts[0]); }).then(function(result){ - assert.equal(result.toNumber(), 500000, 'accounts[0] balance is wrong'); + Assert.AreEqual(result.toNumber(), 500000, 'accounts[0] balance is wrong'); return token.balanceOf.call("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"); }).then(function(result){ - assert.equal(result.toNumber(), 500000, 'accounts[1] balance is wrong'); + Assert.AreEqual(result.toNumber(), 500000, 'accounts[1] balance is wrong'); }) }); }); diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/Catalyst.Core.Modules.Ledger.Tests.csproj b/src/Catalyst.Core.Modules.Ledger.Tests/Catalyst.Core.Modules.Ledger.Tests.csproj index 158ee14178..ea9fdba9f2 100644 --- a/src/Catalyst.Core.Modules.Ledger.Tests/Catalyst.Core.Modules.Ledger.Tests.csproj +++ b/src/Catalyst.Core.Modules.Ledger.Tests/Catalyst.Core.Modules.Ledger.Tests.csproj @@ -1,20 +1,27 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Ledger.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Ledger.Tests.snk true - + + 1701;1702;VSTHRD200;CS8002 + - - - - + + + + + + + + + + - diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/LedgerKvmTests.cs b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/LedgerKvmTests.cs index 28f3b81002..0b73a02718 100644 --- a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/LedgerKvmTests.cs +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/LedgerKvmTests.cs @@ -1,7 +1,7 @@ -#region LICENSE +#region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,66 +29,96 @@ using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.Kvm; using Catalyst.Abstractions.Mempool; +using Catalyst.Abstractions.Sync.Interfaces; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Lib.DAO.Transaction; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.Extensions.Protocol.Wire; +using Catalyst.Core.Lib.Service; using Catalyst.Core.Modules.Cryptography.BulletProofs; +using Catalyst.Core.Modules.Cryptography.BulletProofs.Types; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Core.Modules.Kvm; using Catalyst.Core.Modules.Ledger.Repository; +using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Network; using Catalyst.TestUtils; using FluentAssertions; +using Google.Protobuf; using Google.Protobuf.WellKnownTypes; -using LibP2P; +using Lib.P2P; using Microsoft.Reactive.Testing; +using MultiFormats; +using MultiFormats.Registry; using Nethermind.Core; +using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Core.Specs; +using Nethermind.Db; using Nethermind.Dirichlet.Numerics; +using Nethermind.Evm; using Nethermind.Evm.Tracing; using Nethermind.Logging; -using Nethermind.Store; +using Nethermind.State; using NSubstitute; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; +using SharpRepository.InMemoryRepository; using ILogger = Serilog.ILogger; namespace Catalyst.Core.Modules.Ledger.Tests.IntegrationTests { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class LedgerKvmTests { - private readonly ILogger _logger; - private readonly MultiHash _genesisHash; - private readonly IHashProvider _hashProvider; - private readonly ISpecProvider _specProvider; - private readonly TestScheduler _testScheduler; - private readonly StateProvider _stateProvider; - private readonly ICryptoContext _cryptoContext; - private readonly IAccountRepository _fakeRepository; - private readonly IDeltaHashProvider _deltaHashProvider; - private readonly ILedgerSynchroniser _ledgerSynchroniser; - private readonly IMempool _mempool; - private readonly IDeltaExecutor _deltaExecutor; - private readonly IStorageProvider _storageProvider; - private readonly ISnapshotableDb _stateDb; - private readonly ISnapshotableDb _codeDb; - - public LedgerKvmTests() + private ILogger _logger; + private MultiHash _genesisHash; + private IHashProvider _hashProvider; + private IMapperProvider _mapperProvider; + private ISpecProvider _specProvider; + private TestScheduler _testScheduler; + private StateProvider _stateProvider; + private ICryptoContext _cryptoContext; + private IAccountRepository _fakeRepository; + private IDeltaHashProvider _deltaHashProvider; + private ISynchroniser _synchroniser; + private IMempool _mempool; + private IDeltaExecutor _deltaExecutor; + private IStorageProvider _storageProvider; + private ISnapshotableDb _stateDb; + private ISnapshotableDb _codeDb; + private IDeltaByNumberRepository _deltaByNumber; + private IPrivateKey _senderPrivateKey; + private IPublicKey _senderPublicKey; + private SigningContext _signingContext; + private IDeltaIndexService _deltaIndexService; + private ITransactionRepository _receipts; + private Address _senderAddress; + + [SetUp] + public void Setup() { _testScheduler = new TestScheduler(); _cryptoContext = new FfiWrapper(); _fakeRepository = Substitute.For(); - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + _mapperProvider = new TestMapperProvider(); _genesisHash = _hashProvider.ComputeUtf8MultiHash("genesis"); _logger = Substitute.For(); - _mempool = Substitute.For>(); + _mempool = Substitute.For>(); _deltaHashProvider = Substitute.For(); - _ledgerSynchroniser = Substitute.For(); + _synchroniser = Substitute.For(); + _deltaByNumber = Substitute.For(); + _receipts = Substitute.For(); - _ledgerSynchroniser.DeltaCache.GenesisHash.Returns(_genesisHash); + _synchroniser.DeltaCache.GenesisHash.Returns(_genesisHash); - var stateDbDevice = new MemDb(); - var codeDbDevice = new MemDb(); + MemDb stateDbDevice = new(); + MemDb codeDbDevice = new(); _stateDb = new StateDb(stateDbDevice); _codeDb = new StateDb(codeDbDevice); @@ -96,234 +126,349 @@ public LedgerKvmTests() _stateProvider = new StateProvider(_stateDb, _codeDb, LimboLogs.Instance); _storageProvider = new StorageProvider(_stateDb, _stateProvider, LimboLogs.Instance); - var stateUpdateHashProvider = new StateUpdateHashProvider(); + StateUpdateHashProvider stateUpdateHashProvider = new(); _specProvider = new CatalystSpecProvider(); - var kvm = new KatVirtualMachine(_stateProvider, _storageProvider, stateUpdateHashProvider, _specProvider, LimboLogs.Instance); - _deltaExecutor = new DeltaExecutor(_specProvider, _stateProvider, _storageProvider, kvm, new FfiWrapper(), _logger); + KatVirtualMachine kvm = new(_stateProvider, _storageProvider, stateUpdateHashProvider, _specProvider, _hashProvider, _cryptoContext, + LimboLogs.Instance); + _deltaExecutor = new DeltaExecutor(_specProvider, _stateProvider, _storageProvider, kvm, new FfiWrapper(), + _logger); + + _deltaIndexService = new DeltaIndexService(new InMemoryRepository()); + _senderPrivateKey = _cryptoContext.GetPrivateKeyFromBytes(new byte[32]); + + _senderPublicKey = _senderPrivateKey.GetPublicKey(); + _senderAddress = _senderPublicKey.ToKvmAddress(); + + _signingContext = new SigningContext + { + NetworkType = NetworkType.Devnet, + SignatureType = SignatureType.TransactionPublic + }; } - + private void RunDeltas(Delta delta) { - var hash1 = _hashProvider.ComputeUtf8MultiHash("update"); - var updates = new[] {hash1}; - _ledgerSynchroniser.CacheDeltasBetween(default, default, default) - .ReturnsForAnyArgs(new Cid[] {hash1, _genesisHash}); - _ledgerSynchroniser.DeltaCache.TryGetOrAddConfirmedDelta(Arg.Any(), out Arg.Any()) + var genesisDelta = new Delta + { + TimeStamp = Timestamp.FromDateTime(DateTime.UnixEpoch), + StateRoot = ByteString.CopyFrom(_stateProvider.StateRoot.Bytes), + }; + + MultiHash hash1 = _hashProvider.ComputeUtf8MultiHash("update"); + var updates = new[] + { + hash1 + }; + _synchroniser.CacheDeltasBetween(default, default, default) + .ReturnsForAnyArgs(new Cid[] + { + hash1, _genesisHash + }); + + _synchroniser.DeltaCache.TryGetOrAddConfirmedDelta(Arg.Any(), out Arg.Any()) .Returns(c => { + delta.PreviousDeltaDfsHash = hash1.ToCid().ToArray().ToByteString(); // lol c[1] = delta; return true; - }, c => false); + }, c => + { + c[1] = genesisDelta; + return true; + }); _deltaHashProvider.DeltaHashUpdates.Returns(updates.Select(h => (Cid) h).ToObservable(_testScheduler)); - + // do not remove - it registers with observable so there is a reference to this object held until the test is ended - var classUnderTest = new Ledger(_deltaExecutor, _stateProvider, _storageProvider, _stateDb, _codeDb, _fakeRepository, _deltaHashProvider, _ledgerSynchroniser, _mempool, _logger); + Ledger _ = new(_deltaExecutor, _stateProvider, _storageProvider, _stateDb, _codeDb, + _fakeRepository, _deltaIndexService, _receipts, _deltaHashProvider, _synchroniser, _mempool, _mapperProvider, _hashProvider, _logger); _testScheduler.Start(); } - [Fact] + [Test] public void Should_Update_State_On_Contract_Entry() { var recipient = _cryptoContext.GeneratePrivateKey().GetPublicKey(); - var sender = _cryptoContext.GeneratePrivateKey().GetPublicKey(); - _stateProvider.CreateAccount(sender.ToKvmAddress(), 1000); + _stateProvider.CreateAccount(_senderAddress, 1000); _stateProvider.CreateAccount(recipient.ToKvmAddress(), UInt256.Zero); + _stateProvider.RecalculateStateRoot(); + _stateProvider.Commit(CatalystGenesisSpec.Instance); + _stateProvider.CommitTree(); var delta = new Delta { + StateRoot = _stateProvider.StateRoot.ToByteString(), TimeStamp = Timestamp.FromDateTime(DateTime.UtcNow), - ContractEntries = + PublicEntries = { - EntryUtils.PrepareContractEntry(recipient, sender, 7) + EntryUtils.PrepareContractEntry(recipient.ToKvmAddress(), _senderAddress, 7) } }; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); RunDeltas(delta); - _stateProvider.GetAccount(recipient.ToKvmAddress()).Balance.Should().Be(7); + var account = _stateProvider.GetAccount(recipient.ToKvmAddress()); + account.Should().NotBe(null); + account.Balance.Should().Be(7); } - [Fact] + [Test] public void Should_Update_State_On_Public_Entry() { var recipient = _cryptoContext.GeneratePrivateKey().GetPublicKey(); - var sender = _cryptoContext.GeneratePrivateKey().GetPublicKey(); - _stateProvider.CreateAccount(sender.ToKvmAddress(), 1000); + _stateProvider.CreateAccount(_senderAddress, 1000); _stateProvider.CreateAccount(recipient.ToKvmAddress(), UInt256.Zero); + _stateProvider.Commit(CatalystGenesisSpec.Instance); + _stateProvider.CommitTree(); var delta = new Delta { + StateRoot = _stateProvider.StateRoot.ToByteString(), + PreviousDeltaDfsHash = ByteString.CopyFrom(_genesisHash.ToArray()), TimeStamp = Timestamp.FromDateTime(DateTime.UtcNow), PublicEntries = { - EntryUtils.PreparePublicEntry(recipient, sender, 3) + EntryUtils.PreparePublicEntry(recipient, _senderPublicKey, 3) } }; + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); RunDeltas(delta); var account = _stateProvider.GetAccount(recipient.ToKvmAddress()); + account.Should().NotBe(null); account.Balance.Should().Be(3); } - [Fact] + [Test] public void Should_Deploy_Code_On_Code_Entry() { - var sender = _cryptoContext.GeneratePrivateKey().GetPublicKey(); - _stateProvider.CreateAccount(sender.ToKvmAddress(), 1000); + _stateProvider.CreateAccount(_senderAddress, 1000); + _stateProvider.Commit(CatalystGenesisSpec.Instance); + _stateProvider.CommitTree(); - var contractAddress = Address.OfContract(sender.ToKvmAddress(), _stateProvider.GetNonce(sender.ToKvmAddress())); + var contractAddress = ContractAddress.From(_senderAddress, _stateProvider.GetNonce(_senderAddress)); // PUSH1 1 PUSH1 0 MSTORE PUSH1 1 PUSH1 31 RETURN STOP const string initCodeHex = "0x60016000526001601FF300"; var delta = new Delta { + StateRoot = _stateProvider.StateRoot.ToByteString(), TimeStamp = Timestamp.FromDateTime(DateTime.UtcNow), - ContractEntries = + PublicEntries = { - EntryUtils.PrepareContractEntry(null, sender, 5, initCodeHex) + EntryUtils.PrepareContractEntry(null, _senderAddress, 5, initCodeHex) } }; - delta.ContractEntries[0].GasLimit = 1_000_000L; // has to be enough for intrinsic gas 21000 + CREATE + code deposit (~53000) + var senderAccount = _stateProvider.GetAccount(_senderAddress); + senderAccount.Should().NotBe(null); + + delta.PublicEntries[0].GasLimit = + 1_000_000L; // has to be enough for intrinsic gas 21000 + CREATE + code deposit (~53000) + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); RunDeltas(delta); var account = _stateProvider.GetAccount(contractAddress); + account.Should().NotBe(null); + account.Balance.Should().Be(5); _stateProvider.GetCode(account.CodeHash).Should().Equal(Bytes.FromHexString("0x01")); } - [Fact] + [Test] public void Should_Deploy_ERC20() { - var sender = _cryptoContext.GeneratePrivateKey().GetPublicKey(); - _stateProvider.CreateAccount(sender.ToKvmAddress(), 1000); - var contractAddress1 = Address.OfContract(sender.ToKvmAddress(), _stateProvider.GetNonce(sender.ToKvmAddress())); - var contractAddress2 = Address.OfContract(sender.ToKvmAddress(), _stateProvider.GetNonce(sender.ToKvmAddress()) + 2); + _stateProvider.CreateAccount(_senderAddress, 1000); + _stateProvider.Commit(CatalystGenesisSpec.Instance); + _stateProvider.CommitTree(); + + var contractAddress1 = ContractAddress.From(_senderAddress, + _stateProvider.GetNonce(_senderAddress) + 0); + var contractAddress2 = ContractAddress.From(_senderAddress, + _stateProvider.GetNonce(_senderAddress) + 2); - const string migrationInitHex = "608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102f8806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a723058206bf582fa33b86704b115209928731f910b5f9872d5a4c376fe8d639aa373ad790029"; + const string migrationInitHex = + "608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102f8806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a723058206bf582fa33b86704b115209928731f910b5f9872d5a4c376fe8d639aa373ad790029"; const string call1Hex = "fdacd5760000000000000000000000000000000000000000000000000000000000000001"; - const string initCodeHex = "608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820b01e799233fd04eb73bd6896cd148b8926873dc1418dd3e1a44f25a5e59903d2002900000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000034b4154000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034b41540000000000000000000000000000000000000000000000000000000000"; + const string initCodeHex = + "608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820b01e799233fd04eb73bd6896cd148b8926873dc1418dd3e1a44f25a5e59903d2002900000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000034b4154000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034b41540000000000000000000000000000000000000000000000000000000000"; const string call2Hex = "fdacd5760000000000000000000000000000000000000000000000000000000000000001"; // to // 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f var delta = new Delta { + StateRoot = _stateProvider.StateRoot.ToByteString(), TimeStamp = Timestamp.FromDateTime(DateTime.UtcNow), - ContractEntries = + PublicEntries = { - EntryUtils.PrepareContractEntry(null, sender, 0, migrationInitHex), - EntryUtils.PrepareContractEntry(null, sender, 0, call1Hex, 1), - EntryUtils.PrepareContractEntry(null, sender, 0, initCodeHex, 2), - EntryUtils.PrepareContractEntry(null, sender, 0, call2Hex, 3), + EntryUtils.PrepareContractEntry(null, _senderAddress, 0, migrationInitHex), + EntryUtils.PrepareContractEntry(contractAddress1, _senderAddress, 0, call1Hex, 1), + EntryUtils.PrepareContractEntry(null, _senderAddress, 0, initCodeHex, 2), + EntryUtils.PrepareContractEntry(contractAddress1, _senderAddress, 0, call2Hex, 3) } }; - delta.ContractEntries[0].GasLimit = 3_000_000L; // has to be enough for intrinsic gas 21000 + CREATE + code deposit - delta.ContractEntries[1].GasLimit = 100_000L; // has to be enough for intrinsic gas 21000 + call - delta.ContractEntries[2].GasLimit = 3_000_000L; // has to be enough for intrinsic gas 21000 + CREATE + code deposit - delta.ContractEntries[3].GasLimit = 100_000L; // has to be enough for intrinsic gas 21000 + call + delta.PublicEntries[0].GasLimit = + 3_000_000L; // has to be enough for intrinsic gas 21000 + CREATE + code deposit + delta.PublicEntries[1].GasLimit = 100_000L; // has to be enough for intrinsic gas 21000 + call + delta.PublicEntries[2].GasLimit = + 3_000_000L; // has to be enough for intrinsic gas 21000 + CREATE + code deposit + delta.PublicEntries[3].GasLimit = 100_000L; // has to be enough for intrinsic gas 21000 + call + + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); + delta.PublicEntries[1].Signature = delta.PublicEntries[1] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); + delta.PublicEntries[2].Signature = delta.PublicEntries[2] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); + delta.PublicEntries[3].Signature = delta.PublicEntries[3] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); + RunDeltas(delta); var migrationAccount = _stateProvider.GetAccount(contractAddress1); + migrationAccount.Should().NotBe(null); migrationAccount.Balance.Should().Be(0); - _stateProvider.GetCode(migrationAccount.CodeHash).Should().Equal(Bytes.FromHexString("608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a723058206bf582fa33b86704b115209928731f910b5f9872d5a4c376fe8d639aa373ad790029")); + _stateProvider.GetCode(migrationAccount.CodeHash).Should().Equal(Bytes.FromHexString( + "608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a723058206bf582fa33b86704b115209928731f910b5f9872d5a4c376fe8d639aa373ad790029")); var erc20Account = _stateProvider.GetAccount(contractAddress2); + erc20Account.Should().NotBe(null); + erc20Account.Balance.Should().Be(0); - _stateProvider.GetCode(erc20Account.CodeHash).Should().Equal(Bytes.FromHexString("6080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820b01e799233fd04eb73bd6896cd148b8926873dc1418dd3e1a44f25a5e59903d20029")); + _stateProvider.GetCode(erc20Account.CodeHash).Should().Equal(Bytes.FromHexString( + "6080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820b01e799233fd04eb73bd6896cd148b8926873dc1418dd3e1a44f25a5e59903d20029")); } - [Fact] + [Test] public void Should_Deploy_ERC20_and_ask_about_balance() { - var sender = _cryptoContext.GeneratePrivateKey().GetPublicKey(); - _stateProvider.CreateAccount(sender.ToKvmAddress(), 1000); - var contractAddress1 = Address.OfContract(sender.ToKvmAddress(), _stateProvider.GetNonce(sender.ToKvmAddress())); - var contractAddress2 = Address.OfContract(sender.ToKvmAddress(), _stateProvider.GetNonce(sender.ToKvmAddress()) + 2); + _stateProvider.CreateAccount(_senderAddress, 1000); + _stateProvider.Commit(CatalystGenesisSpec.Instance); + _stateProvider.CommitTree(); + + var contractAddress1 = ContractAddress.From(_senderAddress, + _stateProvider.GetNonce(_senderAddress)); + var contractAddress2 = ContractAddress.From(_senderAddress, + _stateProvider.GetNonce(_senderAddress) + 2); // migration contract - const string migrationInitHex = "608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102f8806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a723058206bf582fa33b86704b115209928731f910b5f9872d5a4c376fe8d639aa373ad790029"; - + const string migrationInitHex = + "608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102f8806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a723058206bf582fa33b86704b115209928731f910b5f9872d5a4c376fe8d639aa373ad790029"; + // migration call const string call1Hex = "fdacd5760000000000000000000000000000000000000000000000000000000000000001"; - + // erc20 contract deployment - const string initCodeHex = "608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820b01e799233fd04eb73bd6896cd148b8926873dc1418dd3e1a44f25a5e59903d2002900000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000034b4154000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034b41540000000000000000000000000000000000000000000000000000000000"; - + const string initCodeHex = + "608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820b01e799233fd04eb73bd6896cd148b8926873dc1418dd3e1a44f25a5e59903d2002900000000000000000000000000000000000000000000000000000000000f42400000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000000800000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000034b4154000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000034b41540000000000000000000000000000000000000000000000000000000000"; + // migration call const string call2Hex = "fdacd5760000000000000000000000000000000000000000000000000000000000000001"; - const string transfer = "a9059cbb000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa000000000000000000000000000000000000000000000000000000000007a120"; - + const string transfer = + "a9059cbb000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa000000000000000000000000000000000000000000000000000000000007a120"; + + // _stateProvider.UpdateRootHash(); after Nethermind pull var delta = new Delta { + StateRoot = _stateProvider.StateRoot.ToByteString(), + PreviousDeltaDfsHash = ByteString.CopyFrom(new byte[32]), TimeStamp = Timestamp.FromDateTime(DateTime.UtcNow), - ContractEntries = + PublicEntries = { - EntryUtils.PrepareContractEntry(null, sender, 0, migrationInitHex), - EntryUtils.PrepareContractEntry(null, sender, 0, call1Hex, 1), - EntryUtils.PrepareContractEntry(null, sender, 0, initCodeHex, 2), - EntryUtils.PrepareContractEntry(null, sender, 0, call2Hex, 3), - EntryUtils.PrepareContractEntry(sender, sender, 0, transfer, 4) + EntryUtils.PrepareContractEntry(null, _senderAddress, 0, migrationInitHex), + EntryUtils.PrepareContractEntry(contractAddress1, _senderAddress, 0, call1Hex, 1), + EntryUtils.PrepareContractEntry(null, _senderAddress, 0, initCodeHex, 2), + EntryUtils.PrepareContractEntry(contractAddress1, _senderAddress, 0, call2Hex, 3), + EntryUtils.PrepareContractEntry(contractAddress2, _senderAddress, 0, transfer, 4) } }; - delta.ContractEntries[0].GasLimit = 3_000_000L; // has to be enough for intrinsic gas 21000 + CREATE + code deposit - delta.ContractEntries[1].GasLimit = 100_000L; // has to be enough for intrinsic gas 21000 + call - delta.ContractEntries[2].GasLimit = 3_000_000L; // has to be enough for intrinsic gas 21000 + CREATE + code deposit - delta.ContractEntries[3].GasLimit = 100_000L; // has to be enough for intrinsic gas 21000 + call - delta.ContractEntries[4].GasLimit = 200_000; - delta.ContractEntries[4].TargetContract = contractAddress2.Bytes; + delta.PublicEntries[0].GasLimit = + 3_000_000L; // has to be enough for intrinsic gas 21000 + CREATE + code deposit + delta.PublicEntries[1].GasLimit = 100_000L; // has to be enough for intrinsic gas 21000 + call + delta.PublicEntries[2].GasLimit = + 3_000_000L; // has to be enough for intrinsic gas 21000 + CREATE + code deposit + delta.PublicEntries[3].GasLimit = 100_000L; // has to be enough for intrinsic gas 21000 + call + delta.PublicEntries[4].GasLimit = 200_000; + delta.PublicEntries[4].ReceiverAddress = contractAddress2.Bytes.ToByteString(); + + delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); + delta.PublicEntries[1].Signature = delta.PublicEntries[1] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); + delta.PublicEntries[2].Signature = delta.PublicEntries[2] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); + delta.PublicEntries[3].Signature = delta.PublicEntries[3] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); + delta.PublicEntries[4].Signature = delta.PublicEntries[4] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); + RunDeltas(delta); var migrationAccount = _stateProvider.GetAccount(contractAddress1); + migrationAccount.Should().NotBe(null); migrationAccount.Balance.Should().Be(0); - _stateProvider.GetCode(migrationAccount.CodeHash).Should().Equal(Bytes.FromHexString("608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a723058206bf582fa33b86704b115209928731f910b5f9872d5a4c376fe8d639aa373ad790029")); + _stateProvider.GetCode(migrationAccount.CodeHash).Should().Equal(Bytes.FromHexString( + "608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a723058206bf582fa33b86704b115209928731f910b5f9872d5a4c376fe8d639aa373ad790029")); var erc20Account = _stateProvider.GetAccount(contractAddress2); erc20Account.Balance.Should().Be(0); - _stateProvider.GetCode(erc20Account.CodeHash).Should().Equal(Bytes.FromHexString("6080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820b01e799233fd04eb73bd6896cd148b8926873dc1418dd3e1a44f25a5e59903d20029")); - + _stateProvider.GetCode(erc20Account.CodeHash).Should().Equal(Bytes.FromHexString( + "6080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a72305820b01e799233fd04eb73bd6896cd148b8926873dc1418dd3e1a44f25a5e59903d20029")); + // transfer of 500k to aaa...aaa (only here because of the issues with recipient address for smart contracts) - _stateProvider.GetAccount(sender.ToKvmAddress()).Nonce.Should().Be(5); - + _stateProvider.GetAccount(_senderAddress).Nonce.Should().Be(5); + // need to commit changes as the next two calls are reverting changes on the same state provider _storageProvider.Commit(); + _stateProvider.RecalculateStateRoot(); _stateProvider.Commit(_specProvider.GetSpec(1)); _storageProvider.CommitTrees(); _stateProvider.CommitTree(); // balance should be 500_000 0x7a120 - var balanceOf1 = "70a08231000000000000000000000000" + sender.ToKvmAddress().ToString(false, false); - var balanceOf1Tracer = new CallOutputTracer(); - var balanceOf1Delta = EntryUtils.PrepareSingleContractEntryDelta(sender, sender, 0, balanceOf1, 5); - balanceOf1Delta.ContractEntries[0].TargetContract = contractAddress2.Bytes; - balanceOf1Delta.ContractEntries[0].GasLimit = 200_000; - _deltaExecutor.CallAndRestore(balanceOf1Delta, balanceOf1Tracer); + var balanceOf1 = "70a08231000000000000000000000000" + + _senderAddress.ToString(false, false); + CallOutputTracer balanceOf1Tracer = new(); + var balanceOf1Delta = + EntryUtils.PrepareSingleContractEntryDelta(_senderPublicKey, _senderPublicKey, 0, balanceOf1, 5); + balanceOf1Delta.PublicEntries[0].ReceiverAddress = contractAddress2.Bytes.ToByteString(); + balanceOf1Delta.PublicEntries[0].GasLimit = 200_000; + balanceOf1Delta.PublicEntries[0].Signature = delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); + _deltaExecutor.CallAndReset(balanceOf1Delta, balanceOf1Tracer); balanceOf1Tracer.StatusCode.Should().Be(1); balanceOf1Tracer.ReturnValue.Should().Equal(Bytes.FromHexString("0x7a120").PadLeft(32)); - + // potential bug in Nethermind - _stateProvider.GetAccount(sender.ToKvmAddress()).Nonce.Should().Be(6); - + _stateProvider.GetAccount(_senderAddress).Nonce.Should().Be(6); + // balance should be 500_000 0x7a120 const string balanceOf2 = "70a08231000000000000000000000000aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; - var balanceOf2Delta = EntryUtils.PrepareSingleContractEntryDelta(sender, sender, 0, balanceOf2, 6); - balanceOf2Delta.ContractEntries[0].GasLimit = 200_000; - balanceOf2Delta.ContractEntries[0].TargetContract = contractAddress2.Bytes; - var balanceOf2Tracer = new CallOutputTracer(); - _deltaExecutor.CallAndRestore(balanceOf2Delta, balanceOf2Tracer); + var balanceOf2Delta = + EntryUtils.PrepareSingleContractEntryDelta(_senderPublicKey, _senderPublicKey, 0, balanceOf2, 6); + balanceOf2Delta.PublicEntries[0].GasLimit = 200_000; + balanceOf2Delta.PublicEntries[0].ReceiverAddress = contractAddress2.Bytes.ToByteString(); + balanceOf2Delta.PublicEntries[0].Signature = balanceOf2Delta.PublicEntries[0] + .GenerateSignature(_cryptoContext, _senderPrivateKey, _signingContext); + CallOutputTracer balanceOf2Tracer = new(); + _deltaExecutor.CallAndReset(balanceOf2Delta, balanceOf2Tracer); balanceOf2Tracer.StatusCode.Should().Be(1); balanceOf2Tracer.ReturnValue.Should().Equal(Bytes.FromHexString("0x7a120").PadLeft(32)); - + // potential bug in Nethermind - _stateProvider.GetAccount(sender.ToKvmAddress()).Nonce.Should().Be(7); + _stateProvider.GetAccount(_senderAddress).Nonce.Should().Be(7); } } } diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/.gitignore b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/.gitignore new file mode 100644 index 0000000000..216d3d0e03 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/.gitignore @@ -0,0 +1,62 @@ +# Logs +logs +*.log +npm-debug.log* +yarn-debug.log* +yarn-error.log* + +# Runtime data +pids +*.pid +*.seed +*.pid.lock + +# Directory for instrumented libs generated by jscoverage/JSCover +lib-cov + +# Coverage directory used by tools like istanbul +coverage + +# nyc test coverage +.nyc_output + +# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) +.grunt + +# Bower dependency directory (https://bower.io/) +bower_components + +# node-waf configuration +.lock-wscript + +# Compiled binary addons (https://nodejs.org/api/addons.html) +build/Release + +# Dependency directories +node_modules/ +jspm_packages/ + +# TypeScript v1 declaration files +typings/ + +# Optional npm cache directory +.npm + +# Optional eslint cache +.eslintcache + +# Optional REPL history +.node_repl_history + +# Output of 'npm pack' +*.tgz + +# Yarn Integrity file +.yarn-integrity + +# dotenv environment variables file +.env + +# next.js build output +.next +.DS_Store diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/.secret b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/.secret new file mode 100644 index 0000000000..1b5273eb17 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/.secret @@ -0,0 +1 @@ +silly funny task remove diamond maximum rack awesome sting chalk recycle also social banner verify \ No newline at end of file diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/cli-scripts.txt b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/cli-scripts.txt new file mode 100644 index 0000000000..802771fdf7 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/cli-scripts.txt @@ -0,0 +1,52 @@ +function init() { + net.version + eth.blocknumber + eth.getBlockByNumber("latest", true) +} + +var sender = "0xb77aec9f59f9d6f39793289a09aea871932619ed" + +function storage() { + eth.getStorageAt("0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7", "0x9557b59d272c7577c2a196e993fe53a09330cfa9c5e07638a1e4adff4b67fb54","0x0") + eth.getStorageAt("0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7", "0x0","0x0") + eth.getStorageAt("0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7", "0x3","0x0") + eth.getStorageAt("0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7", "0x3","0x0") + eth.getStorageAt("0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7", "0x4","0x0") + eth.getStorageAt("0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7", "0x4","0x0") + eth.getStorageAt("0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7", "0x5","0x0") + eth.getStorageAt("0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7", "0x5","0x0") +} + +function state() { + eth.getCode(sender, "0x0") + eth.getBalance(sender, "0x0") + eth.getTransactionCount(sender, "0x0") +} + +var txHash; + +function sendTx() { + eth.estimateGas({ + "from": sender, + "gas": "0x6691b7", + "gasPrice": "0x4a817c800", + "data": "0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102f8806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a72305820e67b04177cff238ccecd05d93dbbf6b4d7dc6417aeafd6361dae8a04506a8bed0029" + }); + + txHash = eth.sendRawTransaction("0xf903ab808504a817c800836691b78080b90358608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102f8806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a72305820e67b04177cff238ccecd05d93dbbf6b4d7dc6417aeafd6361dae8a04506a8bed00291ca0f8e68ed4b2d736172fb1f54f5a700be605e281db9686141fcaf8c07fc792a204a06a0a8d181eb07397829a05d875cb662542a3de79390eb22dff78776f9c37aaac"); +} + +var receipt; +var tx; + +function getReceipt() { + receipt = eth.getTransactionReceipt(txHash); +} + +var contract = {}; + +function check() { + contract.Code = eth.getCode(receipt.contractAddress,"0x0"); + contract.Balance = eth.getBalance(receipt.contractAddress,"0x0"); + tx = eth.getTransactionByHash(txHash) +} \ No newline at end of file diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/EIP20.sol b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/EIP20.sol new file mode 100644 index 0000000000..2a75443c76 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/EIP20.sol @@ -0,0 +1,71 @@ +/* +Implements EIP20 token standard: https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md +.*/ + +pragma solidity ^0.4.24; + +import "./EIP20Interface.sol"; + + +contract EIP20 is EIP20Interface { + + uint256 constant private MAX_UINT256 = 2**256 - 1; + mapping (address => uint256) public balances; + mapping (address => mapping (address => uint256)) public allowed; + /* + NOTE: + The following variables are OPTIONAL vanities. One does not have to include them. + They allow one to customise the token contract & in no way influences the core functionality. + Some wallets/interfaces might not even bother to look at this information. + */ + string public name; //fancy name: eg Simon Bucks + uint8 public decimals; //How many decimals to show. + string public symbol; //An identifier: eg SBX + + constructor ( + uint256 _initialAmount, + string _tokenName, + uint8 _decimalUnits, + string _tokenSymbol + ) public { + balances[msg.sender] = _initialAmount; // Give the creator all initial tokens + totalSupply = _initialAmount; // Update total supply + name = _tokenName; // Set the name for display purposes + decimals = _decimalUnits; // Amount of decimals for display purposes + symbol = _tokenSymbol; // Set the symbol for display purposes + } + + function transfer(address _to, uint256 _value) public returns (bool success) { + require(balances[msg.sender] >= _value); + balances[msg.sender] -= _value; + balances[_to] += _value; + emit Transfer(msg.sender, _to, _value); //solhint-disable-line indent, no-unused-vars + return true; + } + + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success) { + uint256 allowance = allowed[_from][msg.sender]; + require(balances[_from] >= _value && allowance >= _value); + balances[_to] += _value; + balances[_from] -= _value; + if (allowance < MAX_UINT256) { + allowed[_from][msg.sender] -= _value; + } + emit Transfer(_from, _to, _value); //solhint-disable-line indent, no-unused-vars + return true; + } + + function balanceOf(address _owner) public view returns (uint256 balance) { + return balances[_owner]; + } + + function approve(address _spender, uint256 _value) public returns (bool success) { + allowed[msg.sender][_spender] = _value; + emit Approval(msg.sender, _spender, _value); //solhint-disable-line indent, no-unused-vars + return true; + } + + function allowance(address _owner, address _spender) public view returns (uint256 remaining) { + return allowed[_owner][_spender]; + } +} diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/EIP20Interface.sol b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/EIP20Interface.sol new file mode 100644 index 0000000000..06b231026a --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/EIP20Interface.sol @@ -0,0 +1,49 @@ +// Abstract contract for the full ERC 20 Token standard +// https://github.com/ethereum/EIPs/blob/master/EIPS/eip-20.md +pragma solidity ^0.4.24; + +contract EIP20Interface { + /* This is a slight change to the ERC20 base standard. + function totalSupply() constant returns (uint256 supply); + is replaced with: + uint256 public totalSupply; + This automatically creates a getter function for the totalSupply. + This is moved to the base contract since public getter functions are not + currently recognised as an implementation of the matching abstract + function by the compiler. + */ + /// total amount of tokens + uint256 public totalSupply; + + /// @param _owner The address from which the balance will be retrieved + /// @return The balance + function balanceOf(address _owner) public view returns (uint256 balance); + + /// @notice send `_value` token to `_to` from `msg.sender` + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return Whether the transfer was successful or not + function transfer(address _to, uint256 _value) public returns (bool success); + + /// @notice send `_value` token to `_to` from `_from` on the condition it is approved by `_from` + /// @param _from The address of the sender + /// @param _to The address of the recipient + /// @param _value The amount of token to be transferred + /// @return Whether the transfer was successful or not + function transferFrom(address _from, address _to, uint256 _value) public returns (bool success); + + /// @notice `msg.sender` approves `_spender` to spend `_value` tokens + /// @param _spender The address of the account able to transfer the tokens + /// @param _value The amount of tokens to be approved for transfer + /// @return Whether the approval was successful or not + function approve(address _spender, uint256 _value) public returns (bool success); + + /// @param _owner The address of the account owning tokens + /// @param _spender The address of the account able to transfer the tokens + /// @return Amount of remaining tokens allowed to spent + function allowance(address _owner, address _spender) public view returns (uint256 remaining); + + // solhint-disable-next-line no-simple-event-func-name + event Transfer(address indexed _from, address indexed _to, uint256 _value); + event Approval(address indexed _owner, address indexed _spender, uint256 _value); +} diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/Migrations.sol b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/Migrations.sol new file mode 100644 index 0000000000..c378ffb028 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/Migrations.sol @@ -0,0 +1,23 @@ +pragma solidity >=0.4.21 <0.6.0; + +contract Migrations { + address public owner; + uint public last_completed_migration; + + constructor() public { + owner = msg.sender; + } + + modifier restricted() { + if (msg.sender == owner) _; + } + + function setCompleted(uint completed) public restricted { + last_completed_migration = completed; + } + + function upgrade(address new_address) public restricted { + Migrations upgraded = Migrations(new_address); + upgraded.setCompleted(last_completed_migration); + } +} diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/OwnedSet.sol b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/OwnedSet.sol new file mode 100644 index 0000000000..754c9204bf --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/OwnedSet.sol @@ -0,0 +1,76 @@ +// Copyright 2017 Peter Czaban, Parity Technologies Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// An owned validator set contract where the owner can add or remove validators. + +pragma solidity ^0.4.22; + +import "./interfaces/BaseOwnedSet.sol"; +import "./interfaces/ValidatorSet.sol"; + + +contract OwnedSet is ValidatorSet, BaseOwnedSet { + // STATE + + // System address, used by the block sealer. + address public systemAddress; + + // MODIFIERS + modifier onlySystem() { + require(msg.sender == systemAddress); + _; + } + + constructor(address[] _initial) BaseOwnedSet(_initial) + public + { + systemAddress = 0xffffFFFfFFffffffffffffffFfFFFfffFFFfFFfE; + } + + // Called when an initiated change reaches finality and is activated. + function finalizeChange() + external + onlySystem + { + baseFinalizeChange(); + } + + // MISBEHAVIOUR HANDLING + + function reportBenign(address _validator, uint256 _blockNumber) + external + { + baseReportBenign(msg.sender, _validator, _blockNumber); + } + + function reportMalicious(address _validator, uint256 _blockNumber, bytes _proof) + external + { + baseReportMalicious( + msg.sender, + _validator, + _blockNumber, + _proof + ); + } + + // PRIVATE + + // Log desire to change the current list. + function initiateChange() + private + { + emit InitiateChange(blockhash(block.number - 1), pending); + } +} diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/interfaces/BaseOwnedSet.sol b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/interfaces/BaseOwnedSet.sol new file mode 100644 index 0000000000..819079dfc3 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/interfaces/BaseOwnedSet.sol @@ -0,0 +1,215 @@ +// Copyright 2018, Parity Technologies Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +// An owned validator set contract where the owner can add or remove validators. +// This is an abstract contract that provides the base logic for adding/removing +// validators and provides base implementations for the `ValidatorSet` +// interface. The base implementations of the misbehavior reporting functions +// perform validation on the reported and reporter validators according to the +// currently active validator set. The base implementation of `finalizeChange` +// validates that there are existing unfinalized changes. + +pragma solidity ^0.4.22; + +import "./Owned.sol"; + + +contract BaseOwnedSet is Owned { + // EVENTS + event ChangeFinalized(address[] currentSet); + + // STATE + + // Was the last validator change finalized. Implies validators == pending + bool public finalized; + + // TYPES + struct AddressStatus { + bool isIn; + uint index; + } + + // EVENTS + event Report(address indexed reporter, address indexed reported, bool indexed malicious); + + // STATE + uint public recentBlocks = 20; + + // Current list of addresses entitled to participate in the consensus. + address[] validators; + address[] pending; + mapping(address => AddressStatus) status; + + // MODIFIERS + + /// Asserts whether a given address is currently a validator. A validator + /// that is pending to be added is not considered a validator, only when + /// that change is finalized will this method return true. A validator that + /// is pending to be removed is immediately not considered a validator + /// (before the change is finalized). + /// + /// For the purposes of this contract one of the consequences is that you + /// can't report on a validator that is currently active but pending to be + /// removed. This is a compromise for simplicity since the reporting + /// functions only emit events which can be tracked off-chain. + modifier isValidator(address _someone) { + bool isIn = status[_someone].isIn; + uint index = status[_someone].index; + + require(isIn && index < validators.length && validators[index] == _someone); + _; + } + + modifier isNotValidator(address _someone) { + require(!status[_someone].isIn); + _; + } + + modifier isRecent(uint _blockNumber) { + require(block.number <= _blockNumber + recentBlocks && _blockNumber < block.number); + _; + } + + modifier whenFinalized() { + require(finalized); + _; + } + + modifier whenNotFinalized() { + require(!finalized); + _; + } + + constructor(address[] _initial) + public + { + pending = _initial; + for (uint i = 0; i < _initial.length; i++) { + status[_initial[i]].isIn = true; + status[_initial[i]].index = i; + } + validators = pending; + } + + // OWNER FUNCTIONS + + // Add a validator. + function addValidator(address _validator) + external + onlyOwner + isNotValidator(_validator) + { + status[_validator].isIn = true; + status[_validator].index = pending.length; + pending.push(_validator); + triggerChange(); + } + + // Remove a validator. + function removeValidator(address _validator) + external + onlyOwner + isValidator(_validator) + { + // Remove validator from pending by moving the + // last element to its slot + uint index = status[_validator].index; + pending[index] = pending[pending.length - 1]; + status[pending[index]].index = index; + delete pending[pending.length - 1]; + pending.length--; + + // Reset address status + delete status[_validator]; + + triggerChange(); + } + + function setRecentBlocks(uint _recentBlocks) + external + onlyOwner + { + recentBlocks = _recentBlocks; + } + + // GETTERS + + // Called to determine the current set of validators. + function getValidators() + external + view + returns (address[]) + { + return validators; + } + + // Called to determine the pending set of validators. + function getPending() + external + view + returns (address[]) + { + return pending; + } + + // INTERNAL + + // Report that a validator has misbehaved in a benign way. + function baseReportBenign(address _reporter, address _validator, uint _blockNumber) + internal + isValidator(_reporter) + isValidator(_validator) + isRecent(_blockNumber) + { + emit Report(_reporter, _validator, false); + } + + // Report that a validator has misbehaved maliciously. + function baseReportMalicious( + address _reporter, + address _validator, + uint _blockNumber, + bytes _proof + ) + internal + isValidator(_reporter) + isValidator(_validator) + isRecent(_blockNumber) + { + emit Report(_reporter, _validator, true); + } + + // Called when an initiated change reaches finality and is activated. + function baseFinalizeChange() + internal + whenNotFinalized + { + validators = pending; + finalized = true; + emit ChangeFinalized(validators); + } + + // PRIVATE + + function triggerChange() + private + whenFinalized + { + finalized = false; + initiateChange(); + } + + function initiateChange() + private; +} diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/interfaces/Owned.sol b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/interfaces/Owned.sol new file mode 100644 index 0000000000..b7a956c524 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/interfaces/Owned.sol @@ -0,0 +1,37 @@ +// The owned contract. +// +// Copyright 2016 Gavin Wood, Parity Technologies Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pragma solidity ^0.4.22; + + +contract Owned { + event NewOwner(address indexed old, address indexed current); + + address public owner = msg.sender; + + modifier onlyOwner { + require(msg.sender == owner); + _; + } + + function setOwner(address _new) + external + onlyOwner + { + emit NewOwner(owner, _new); + owner = _new; + } +} diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/interfaces/ValidatorSet.sol b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/interfaces/ValidatorSet.sol new file mode 100644 index 0000000000..dad414956e --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/contracts/kovan/interfaces/ValidatorSet.sol @@ -0,0 +1,55 @@ +// Copyright 2017 Peter Czaban, Parity Technologies Ltd. +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. + +pragma solidity ^0.4.22; + + +interface ValidatorSet { + /// Issue this log event to signal a desired change in validator set. + /// This will not lead to a change in active validator set until + /// finalizeChange is called. + /// + /// Only the last log event of any block can take effect. + /// If a signal is issued while another is being finalized it may never + /// take effect. + /// + /// _parentHash here should be the parent block hash, or the + /// signal will not be recognized. + event InitiateChange(bytes32 indexed _parentHash, address[] _newSet); + + /// Called when an initiated change reaches finality and is activated. + /// Only valid when msg.sender == SYSTEM (EIP96, 2**160 - 2). + /// + /// Also called when the contract is first enabled for consensus. In this case, + /// the "change" finalized is the activation of the initial set. + function finalizeChange() + external; + + /// Reports benign misbehavior of validator of the current validator set + /// (e.g. validator offline). + function reportBenign(address validator, uint256 blockNumber) + external; + + /// Reports malicious misbehavior of validator of the current validator set + /// and provides proof of that misbehavor, which varies by engine + /// (e.g. double vote). + function reportMalicious(address validator, uint256 blockNumber, bytes proof) + external; + + /// Get current validator set (last enacted or initial if no changes ever made). + function getValidators() + external + view + returns (address[]); +} diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/example.secret b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/example.secret new file mode 100644 index 0000000000..1b5273eb17 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/example.secret @@ -0,0 +1 @@ +silly funny task remove diamond maximum rack awesome sting chalk recycle also social banner verify \ No newline at end of file diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/migrations/1_initial_migration.js b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/migrations/1_initial_migration.js new file mode 100644 index 0000000000..ee2135d295 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/migrations/1_initial_migration.js @@ -0,0 +1,5 @@ +const Migrations = artifacts.require("Migrations"); + +module.exports = function(deployer) { + deployer.deploy(Migrations); +}; diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/migrations/2_deploy_contracts.js b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/migrations/2_deploy_contracts.js new file mode 100644 index 0000000000..4f5cf0ac80 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/migrations/2_deploy_contracts.js @@ -0,0 +1,5 @@ +var EIP20 = artifacts.require("EIP20"); + +module.exports = function(deployer) { + deployer.deploy(EIP20, 100, "TestEIP20", 18, "EIP20"); +}; \ No newline at end of file diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/migrations/3_deploy_kovan.js b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/migrations/3_deploy_kovan.js new file mode 100644 index 0000000000..30532b11c8 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/migrations/3_deploy_kovan.js @@ -0,0 +1,5 @@ +var Kovan = artifacts.require("kovan/OwnedSet"); + +module.exports = function(deployer) { + deployer.deploy(Kovan, ["0x5fe351dd36e699b1c87b48199a1739d4939fdcbe"]); +}; diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/package-lock.json b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/package-lock.json new file mode 100644 index 0000000000..2dc1b060cc --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/package-lock.json @@ -0,0 +1,5465 @@ +{ + "name": "truffletest", + "version": "1.0.0", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@babel/helper-module-imports": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz", + "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==", + "requires": { + "@babel/types": "^7.10.4" + } + }, + "@babel/helper-plugin-utils": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz", + "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==" + }, + "@babel/helper-validator-identifier": { + "version": "7.10.4", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz", + "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==" + }, + "@babel/plugin-transform-runtime": { + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.11.5.tgz", + "integrity": "sha512-9aIoee+EhjySZ6vY5hnLjigHzunBlscx9ANKutkeWTJTx6m5Rbq6Ic01tLvO54lSusR+BxV7u4UDdCmXv5aagg==", + "requires": { + "@babel/helper-module-imports": "^7.10.4", + "@babel/helper-plugin-utils": "^7.10.4", + "resolve": "^1.8.1", + "semver": "^5.5.1" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "@babel/runtime": { + "version": "7.11.2", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz", + "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==", + "requires": { + "regenerator-runtime": "^0.13.4" + } + }, + "@babel/types": { + "version": "7.11.5", + "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.5.tgz", + "integrity": "sha512-bvM7Qz6eKnJVFIn+1LPtjlBFPVN5jNDc1XmN15vWe7Q3DPBufWWsLiIvUu7xW87uTG6QoggpIDnUgLQvPheU+Q==", + "requires": { + "@babel/helper-validator-identifier": "^7.10.4", + "lodash": "^4.17.19", + "to-fast-properties": "^2.0.0" + } + }, + "@catalyst-net-js/common": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@catalyst-net-js/common/-/common-0.3.7.tgz", + "integrity": "sha512-tqFqsmtG2IabCLjeNIHj5dS4G4oaFvMYgow1phl5FDidlPg0xsuo7Bx5lfNBVSIRpex3Mam7vCHaOBGqvy8OWg==", + "requires": { + "base32-decode": "^1.0.0", + "base32-encode": "^1.1.1", + "ethereumjs-util": "^6.2.0", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.2" + } + }, + "@catalyst-net-js/truffle-provider": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@catalyst-net-js/truffle-provider/-/truffle-provider-0.3.9.tgz", + "integrity": "sha512-Rmv8Mge1/fPo7dwsgvUaUqo15gNs8xcZAnWKu9MNv0GHZ2rRIoZYQ80/UKfs2kFVSR6JPKnjS3aq0p6G0xmOPw==", + "requires": { + "@catalyst-net-js/common": "^0.3.7", + "@catalyst-net-js/tx": "^0.3.9", + "@catalyst-net-js/wallet": "^0.3.7", + "@catalyst-net-js/wasm-ed25519ph": "^0.3.7", + "@catalystnetwork/protocol-sdk": "^0.0.7", + "@truffle/provider": "^0.2.3", + "@types/google-protobuf": "^3.7.2", + "@types/web3": "^1.2.2", + "any-promise": "^1.3.0", + "bindings": "^1.5.0", + "bip39": "^2.4.2", + "blake2b": "^2.1.3", + "ed25519-hd-key": "^1.1.0", + "ethereum-protocol": "^1.0.1", + "ethereumjs-tx": "^1.0.0", + "ethereumjs-util": "^6.1.0", + "ethereumjs-wallet": "^0.6.3", + "google-protobuf": "^3.11.1", + "json-stable-stringify": "^1.0.1", + "nacl": "^0.1.3", + "ts-protoc-gen": "^0.12.0", + "tweetnacl": "^1.0.1", + "web3": "1.2.1", + "web3-provider-engine": "git+https://github.com/trufflesuite/provider-engine.git#web3-one", + "xtend": "^4.0.2" + } + }, + "@catalyst-net-js/tx": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@catalyst-net-js/tx/-/tx-0.3.9.tgz", + "integrity": "sha512-1DzFwbE+kMt+AsvjExg0W9J9w/zH/EW8Rn5ibMOr5hPdkh7h8vD07eXpY4nhxnAzdoOHbXgqFd4AZziRAup1ZA==", + "requires": { + "@catalyst-net-js/common": "^0.3.7", + "@catalyst-net-js/wallet": "^0.3.7", + "@catalyst-net-js/wasm-ed25519ph": "^0.3.7", + "@catalystnetwork/protocol-sdk": "^0.0.7", + "base32-decode": "^1.0.0", + "base32-encode": "^1.1.1", + "ethjs-util": "^0.1.6", + "tweetnacl": "^1.0.1" + } + }, + "@catalyst-net-js/wallet": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@catalyst-net-js/wallet/-/wallet-0.3.7.tgz", + "integrity": "sha512-yu6/pcTq2nF2fWT93ilFF3BJzqsTI/kSBGCXs/UuOIgrOMBTOlyvKGLpUui5wP54dAfR6Qncf/10lnlRUycLLg==", + "requires": { + "@catalyst-net-js/common": "^0.3.7", + "@catalyst-net-js/wasm-ed25519ph": "^0.3.7", + "@catalystnetwork/protocol-sdk": "^0.0.7", + "bip39": "^3.0.2", + "ed25519-hd-key": "^1.1.0", + "tweetnacl": "^1.0.1" + }, + "dependencies": { + "@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" + }, + "bip39": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz", + "integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==", + "requires": { + "@types/node": "11.11.6", + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1" + } + } + } + }, + "@catalyst-net-js/wasm-ed25519ph": { + "version": "0.3.7", + "resolved": "https://registry.npmjs.org/@catalyst-net-js/wasm-ed25519ph/-/wasm-ed25519ph-0.3.7.tgz", + "integrity": "sha512-qIKoWVF6XZmmq/ls5bRIOM+5aDFG2C3T6GBw7qLULozNyGeoLTJMG6r52jtx43qZW0DGXFfzv8IGqe0QvCjWZA==" + }, + "@catalystnetwork/protocol-sdk": { + "version": "0.0.7", + "resolved": "https://registry.npmjs.org/@catalystnetwork/protocol-sdk/-/protocol-sdk-0.0.7.tgz", + "integrity": "sha512-CbVaF+qOljSdO0Je4JK5T/wx+Ey8phojHVTu9GF98EwE5mw9FfGtTbzOpM9VQ+HlX87gObiw09VASWIrXKkmPg==", + "requires": { + "@types/google-protobuf": "^3.7.2", + "google-protobuf": "^3.11.2", + "protobufjs": "^6.8.8" + } + }, + "@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha1-m4sMxmPWaafY9vXQiToU00jzD78=" + }, + "@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==" + }, + "@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==" + }, + "@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha1-NVy8mLr61ZePntCV85diHx0Ga3A=" + }, + "@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha1-upn7WYYUr2VwDBYZ/wbUVLDYTEU=", + "requires": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha1-Xp4avctz/Ap8uLKR33jIy9l7h9E=" + }, + "@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha1-/yAOPnzyQp4tyvwRQIKOjMY48Ik=" + }, + "@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha1-bMKyDFya1q0NzP0hynZz2Nf79o0=" + }, + "@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha1-Cf0V8tbTq/qbZbw2ZQbWrXhG/1Q=" + }, + "@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha1-p3c2C1s5oaLlEG+OhY8v0tBgxXA=" + }, + "@sindresorhus/is": { + "version": "0.14.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-0.14.0.tgz", + "integrity": "sha512-9NET910DNaIPngYnLLPeg+Ogzqsi9uM4mSboU5y6p8S5DzMTVEsJZrawi+BoDNUVBa2DhJqQYUFvMDfgU062LQ==" + }, + "@szmarczak/http-timer": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@szmarczak/http-timer/-/http-timer-1.1.2.tgz", + "integrity": "sha512-XIB2XbzHTN6ieIjfIMV9hlVcfPU26s2vafYWQcZHWXHOxiaRZYEDKEwdl129Zyg50+foYV2jCgtrqSA6qNuNSA==", + "requires": { + "defer-to-connect": "^1.0.1" + } + }, + "@truffle/error": { + "version": "0.0.11", + "resolved": "https://registry.npmjs.org/@truffle/error/-/error-0.0.11.tgz", + "integrity": "sha512-ju6TucjlJkfYMmdraYY/IBJaFb+Sa+huhYtOoyOJ+G29KcgytUVnDzKGwC7Kgk6IsxQMm62Mc1E0GZzFbGGipw==" + }, + "@truffle/interface-adapter": { + "version": "0.4.16", + "resolved": "https://registry.npmjs.org/@truffle/interface-adapter/-/interface-adapter-0.4.16.tgz", + "integrity": "sha512-lsxk26Lz/h0n8fe37K1ZxowxokXj0AZeNR10QHltDvkHukuTIC4L6fXvrUi74mCwI9hShl4CSBas1Q8kAyJyOA==", + "requires": { + "bn.js": "^4.11.8", + "ethers": "^4.0.32", + "source-map-support": "^0.5.19", + "web3": "1.2.1" + } + }, + "@truffle/provider": { + "version": "0.2.22", + "resolved": "https://registry.npmjs.org/@truffle/provider/-/provider-0.2.22.tgz", + "integrity": "sha512-pyc+AbJTKwH8n1sTNgl/3YcDKWJfIyDk0VDHR2k87mIZEn0UPCNPDzWHwGWVxNzbyF60dQhXctmU8jOpiJ4idQ==", + "requires": { + "@truffle/error": "^0.0.11", + "@truffle/interface-adapter": "^0.4.16", + "web3": "1.2.1" + } + }, + "@types/bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/@types/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha512-pqr857jrp2kPuO9uRjZ3PwnJTjoQy+fcdxvBTvHm6dkmEL9q+hDD/2j/0ELOBPtPnS8LjCX0gI9nbl8lVkadpg==", + "requires": { + "@types/node": "*" + } + }, + "@types/google-protobuf": { + "version": "3.7.3", + "resolved": "https://registry.npmjs.org/@types/google-protobuf/-/google-protobuf-3.7.3.tgz", + "integrity": "sha512-FRwj40euE2bYkG+0X5w2nEA8yAzgJRcEa7RBd0Gsdkb9/tPM2pctVVAvnOUTbcXo2VmIHPo0Ae94Gl9vRHfKzg==" + }, + "@types/long": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/long/-/long-4.0.1.tgz", + "integrity": "sha512-5tXH6Bx/kNGd3MgffdmP4dy2Z+G4eaXw0SE81Tq3BNadtnMR5/ySMzX4SLEzHJzSmPNn4HIdpQsBvXMUykr58w==" + }, + "@types/node": { + "version": "14.6.4", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.6.4.tgz", + "integrity": "sha512-Wk7nG1JSaMfMpoMJDKUsWYugliB2Vy55pdjLpmLixeyMi7HizW2I/9QoxsPCkXl3dO+ZOVqPumKaDUv5zJu2uQ==" + }, + "@types/pbkdf2": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@types/pbkdf2/-/pbkdf2-3.1.0.tgz", + "integrity": "sha512-Cf63Rv7jCQ0LaL8tNXmEyqTHuIJxRdlS5vMh1mj5voN4+QFhVZnlZruezqpWYDiJ8UTzhP0VmeLXCmBk66YrMQ==", + "requires": { + "@types/node": "*" + } + }, + "@types/secp256k1": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/@types/secp256k1/-/secp256k1-4.0.1.tgz", + "integrity": "sha512-+ZjSA8ELlOp8SlKi0YLB2tz9d5iPNEmOBd+8Rz21wTMdaXQIa9b6TEnD6l5qKOCypE7FSyPyck12qZJxSDNoog==", + "requires": { + "@types/node": "*" + } + }, + "@types/web3": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/@types/web3/-/web3-1.2.2.tgz", + "integrity": "sha512-eFiYJKggNrOl0nsD+9cMh2MLk4zVBfXfGnVeRFbpiZzBE20eet4KLA3fXcjSuHaBn0RnQzwLAGdgzgzdet4C0A==", + "requires": { + "web3": "*" + } + }, + "abstract-leveldown": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.6.3.tgz", + "integrity": "sha512-2++wDf/DYqkPR3o5tbfdhF96EfMApo1GpPfzOsR/ZYXdkSmELlvOOEAl9iKkRsktMPHdGjO4rtkBpf2I7TiTeA==", + "requires": { + "xtend": "~4.0.0" + } + }, + "accepts": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/accepts/-/accepts-1.3.7.tgz", + "integrity": "sha512-Il80Qs2WjYlJIBNzNkK6KYqlVMTbZLXgHx2oT0pU/fjRHyEp+PEfEPY0R3WCwAGVOtauxh1hOxNgIf5bv7dQpA==", + "requires": { + "mime-types": "~2.1.24", + "negotiator": "0.6.2" + } + }, + "aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha1-4h3xCtbCBTKVvLuNq0Cwnb6ofk0=" + }, + "ajv": { + "version": "6.12.4", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.4.tgz", + "integrity": "sha512-eienB2c9qVQs2KWexhkrdMLVDoIQCz5KSeLxwg9Lzk4DOfBtIK9PQwwufcsn1jjGuf9WZmqPMbGxOzfcuphJCQ==", + "requires": { + "fast-deep-equal": "^3.1.1", + "fast-json-stable-stringify": "^2.0.0", + "json-schema-traverse": "^0.4.1", + "uri-js": "^4.2.2" + } + }, + "ansi-regex": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-2.1.1.tgz", + "integrity": "sha1-w7M6te42DYbg5ijwRorn7yfWVN8=" + }, + "ansi-styles": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-2.2.1.tgz", + "integrity": "sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=" + }, + "any-promise": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/any-promise/-/any-promise-1.3.0.tgz", + "integrity": "sha1-q8av7tzqUugJzcA3au0845Y10X8=" + }, + "array-flatten": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/array-flatten/-/array-flatten-1.1.1.tgz", + "integrity": "sha1-ml9pkFGx5wczKPKgCJaLZOopVdI=" + }, + "asn1": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/asn1/-/asn1-0.2.4.tgz", + "integrity": "sha512-jxwzQpLQjSmWXgwaCZE9Nz+glAG01yF1QnWgbhGwHI5A6FRIEY6IVqtHhIepHqI7/kyEyQEagBC5mBEFlIYvdg==", + "requires": { + "safer-buffer": "~2.1.0" + } + }, + "asn1.js": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/asn1.js/-/asn1.js-5.4.1.tgz", + "integrity": "sha512-+I//4cYPccV8LdmBLiX8CYvf9Sp3vQsrqu2QNXRcrbiWvcx/UdlFiqUJJzxRQxgsZmvhXhn4cSKeSmoFjVdupA==", + "requires": { + "bn.js": "^4.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "safer-buffer": "^2.1.0" + } + }, + "assert-plus": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/assert-plus/-/assert-plus-1.0.0.tgz", + "integrity": "sha1-8S4PPF13sLHN2RRpQuTpbB5N1SU=" + }, + "async": { + "version": "2.6.3", + "resolved": "https://registry.npmjs.org/async/-/async-2.6.3.tgz", + "integrity": "sha512-zflvls11DCy+dQWzTW2dzuilv8Z5X/pjfmZOWba6TNIVDm+2UDaJmXSOXlasHKfNBs8oo3M0aT50fDEWfKZjXg==", + "requires": { + "lodash": "^4.17.14" + } + }, + "async-eventemitter": { + "version": "0.2.4", + "resolved": "https://registry.npmjs.org/async-eventemitter/-/async-eventemitter-0.2.4.tgz", + "integrity": "sha512-pd20BwL7Yt1zwDFy+8MX8F1+WCT8aQeKj0kQnTrH9WaeRETlRamVhD0JtRPmrV4GfOJ2F9CvdQkZeZhnh2TuHw==", + "requires": { + "async": "^2.4.0" + } + }, + "async-limiter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/async-limiter/-/async-limiter-1.0.1.tgz", + "integrity": "sha512-csOlWGAcRFJaI6m+F2WKdnMKr4HhdhFVBk0H/QbJFMCr+uO2kwohwXQPxw/9OCxp05r5ghVBFSyioixx3gfkNQ==" + }, + "asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=" + }, + "await-semaphore": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/await-semaphore/-/await-semaphore-0.1.3.tgz", + "integrity": "sha512-d1W2aNSYcz/sxYO4pMGX9vq65qOTu0P800epMud+6cYYX0QcT7zyqcxec3VWzpgvdXo57UWmVbZpLMjX2m1I7Q==" + }, + "aws-sign2": { + "version": "0.7.0", + "resolved": "https://registry.npmjs.org/aws-sign2/-/aws-sign2-0.7.0.tgz", + "integrity": "sha1-tG6JCTSpWR8tL2+G1+ap8bP+dqg=" + }, + "aws4": { + "version": "1.10.1", + "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.1.tgz", + "integrity": "sha512-zg7Hz2k5lI8kb7U32998pRRFin7zJlkfezGJjUc2heaD4Pw2wObakCDVzkKztTm/Ln7eiVvYsjqak0Ed4LkMDA==" + }, + "babel-code-frame": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-code-frame/-/babel-code-frame-6.26.0.tgz", + "integrity": "sha1-Y/1D99weO7fONZR9uP42mj9Yx0s=", + "requires": { + "chalk": "^1.1.3", + "esutils": "^2.0.2", + "js-tokens": "^3.0.2" + } + }, + "babel-core": { + "version": "6.26.3", + "resolved": "https://registry.npmjs.org/babel-core/-/babel-core-6.26.3.tgz", + "integrity": "sha512-6jyFLuDmeidKmUEb3NM+/yawG0M2bDZ9Z1qbZP59cyHLz8kYGKYwpJP0UwUKKUiTRNvxfLesJnTedqczP7cTDA==", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-generator": "^6.26.0", + "babel-helpers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-register": "^6.26.0", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "convert-source-map": "^1.5.1", + "debug": "^2.6.9", + "json5": "^0.5.1", + "lodash": "^4.17.4", + "minimatch": "^3.0.4", + "path-is-absolute": "^1.0.1", + "private": "^0.1.8", + "slash": "^1.0.0", + "source-map": "^0.5.7" + }, + "dependencies": { + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "babel-generator": { + "version": "6.26.1", + "resolved": "https://registry.npmjs.org/babel-generator/-/babel-generator-6.26.1.tgz", + "integrity": "sha512-HyfwY6ApZj7BYTcJURpM5tznulaBvyio7/0d4zFOeMPUmfxkCjHocCuoLa2SAGzBI8AREcH3eP3758F672DppA==", + "requires": { + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "detect-indent": "^4.0.0", + "jsesc": "^1.3.0", + "lodash": "^4.17.4", + "source-map": "^0.5.7", + "trim-right": "^1.0.1" + }, + "dependencies": { + "jsesc": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-1.3.0.tgz", + "integrity": "sha1-RsP+yMGJKxKwgz25vHYiF226s0s=" + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + } + } + }, + "babel-helper-builder-binary-assignment-operator-visitor": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-builder-binary-assignment-operator-visitor/-/babel-helper-builder-binary-assignment-operator-visitor-6.24.1.tgz", + "integrity": "sha1-zORReto1b0IgvK6KAsKzRvmlZmQ=", + "requires": { + "babel-helper-explode-assignable-expression": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-call-delegate": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-call-delegate/-/babel-helper-call-delegate-6.24.1.tgz", + "integrity": "sha1-7Oaqzdx25Bw0YfiL/Fdb0Nqi340=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-define-map": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-define-map/-/babel-helper-define-map-6.26.0.tgz", + "integrity": "sha1-pfVtq0GiX5fstJjH66ypgZ+Vvl8=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-explode-assignable-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-explode-assignable-expression/-/babel-helper-explode-assignable-expression-6.24.1.tgz", + "integrity": "sha1-8luCz33BBDPFX3BZLVdGQArCLKo=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-function-name/-/babel-helper-function-name-6.24.1.tgz", + "integrity": "sha1-00dbjAPtmCQqJbSDUasYOZ01gKk=", + "requires": { + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-get-function-arity": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-get-function-arity/-/babel-helper-get-function-arity-6.24.1.tgz", + "integrity": "sha1-j3eCqpNAfEHTqlCQj4mwMbG2hT0=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-hoist-variables": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-hoist-variables/-/babel-helper-hoist-variables-6.24.1.tgz", + "integrity": "sha1-HssnaJydJVE+rbyZFKc/VAi+enY=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-optimise-call-expression": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-optimise-call-expression/-/babel-helper-optimise-call-expression-6.24.1.tgz", + "integrity": "sha1-96E0J7qfc/j0+pk8VKl4gtEkQlc=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-helper-regex": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-helper-regex/-/babel-helper-regex-6.26.0.tgz", + "integrity": "sha1-MlxZ+QL4LyS3T6zu0DY5VPZJXnI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-helper-remap-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-remap-async-to-generator/-/babel-helper-remap-async-to-generator-6.24.1.tgz", + "integrity": "sha1-XsWBgnrXI/7N04HxySg5BnbkVRs=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helper-replace-supers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helper-replace-supers/-/babel-helper-replace-supers-6.24.1.tgz", + "integrity": "sha1-v22/5Dk40XNpohPKiov3S2qQqxo=", + "requires": { + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-helpers": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-helpers/-/babel-helpers-6.24.1.tgz", + "integrity": "sha1-NHHenK7DiOXIUOWX5Yom3fN2ArI=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-messages": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-messages/-/babel-messages-6.23.0.tgz", + "integrity": "sha1-8830cDhYA1sqKVHG7F7fbGLyYw4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-check-es2015-constants": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-check-es2015-constants/-/babel-plugin-check-es2015-constants-6.22.0.tgz", + "integrity": "sha1-NRV7EBQm/S/9PaP3XH0ekYNbv4o=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-syntax-async-functions": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-async-functions/-/babel-plugin-syntax-async-functions-6.13.0.tgz", + "integrity": "sha1-ytnK0RkbWtY0vzCuCHI5HgZHvpU=" + }, + "babel-plugin-syntax-exponentiation-operator": { + "version": "6.13.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-exponentiation-operator/-/babel-plugin-syntax-exponentiation-operator-6.13.0.tgz", + "integrity": "sha1-nufoM3KQ2pUoggGmpX9BcDF4MN4=" + }, + "babel-plugin-syntax-trailing-function-commas": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-syntax-trailing-function-commas/-/babel-plugin-syntax-trailing-function-commas-6.22.0.tgz", + "integrity": "sha1-ugNgk3+NBuQBgKQ/4NVhb/9TLPM=" + }, + "babel-plugin-transform-async-to-generator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-async-to-generator/-/babel-plugin-transform-async-to-generator-6.24.1.tgz", + "integrity": "sha1-ZTbjeK/2yx1VF6wOQOs+n8jQh2E=", + "requires": { + "babel-helper-remap-async-to-generator": "^6.24.1", + "babel-plugin-syntax-async-functions": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-arrow-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-arrow-functions/-/babel-plugin-transform-es2015-arrow-functions-6.22.0.tgz", + "integrity": "sha1-RSaSy3EdX3ncf4XkQM5BufJE0iE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoped-functions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoped-functions/-/babel-plugin-transform-es2015-block-scoped-functions-6.22.0.tgz", + "integrity": "sha1-u8UbSflk1wy42OC5ToICRs46YUE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-block-scoping": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-block-scoping/-/babel-plugin-transform-es2015-block-scoping-6.26.0.tgz", + "integrity": "sha1-1w9SmcEwjQXBL0Y4E7CgnnOxiV8=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "lodash": "^4.17.4" + } + }, + "babel-plugin-transform-es2015-classes": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-classes/-/babel-plugin-transform-es2015-classes-6.24.1.tgz", + "integrity": "sha1-WkxYpQyclGHlZLSyo7+ryXolhNs=", + "requires": { + "babel-helper-define-map": "^6.24.1", + "babel-helper-function-name": "^6.24.1", + "babel-helper-optimise-call-expression": "^6.24.1", + "babel-helper-replace-supers": "^6.24.1", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-computed-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-computed-properties/-/babel-plugin-transform-es2015-computed-properties-6.24.1.tgz", + "integrity": "sha1-b+Ko0WiV1WNPTNmZttNICjCBWbM=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-destructuring": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-destructuring/-/babel-plugin-transform-es2015-destructuring-6.23.0.tgz", + "integrity": "sha1-mXux8auWf2gtKwh2/jWNYOdlxW0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-duplicate-keys": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-duplicate-keys/-/babel-plugin-transform-es2015-duplicate-keys-6.24.1.tgz", + "integrity": "sha1-c+s9MQypaePvnskcU3QabxV2Qj4=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-for-of": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-for-of/-/babel-plugin-transform-es2015-for-of-6.23.0.tgz", + "integrity": "sha1-9HyVsrYT3x0+zC/bdXNiPHUkhpE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-function-name": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-function-name/-/babel-plugin-transform-es2015-function-name-6.24.1.tgz", + "integrity": "sha1-g0yJhTvDaxrw86TF26qU/Y6sqos=", + "requires": { + "babel-helper-function-name": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-literals/-/babel-plugin-transform-es2015-literals-6.22.0.tgz", + "integrity": "sha1-T1SgLWzWbPkVKAAZox0xklN3yi4=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-modules-amd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-amd/-/babel-plugin-transform-es2015-modules-amd-6.24.1.tgz", + "integrity": "sha1-Oz5UAXI5hC1tGcMBHEvS8AoA0VQ=", + "requires": { + "babel-plugin-transform-es2015-modules-commonjs": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-commonjs": { + "version": "6.26.2", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-commonjs/-/babel-plugin-transform-es2015-modules-commonjs-6.26.2.tgz", + "integrity": "sha512-CV9ROOHEdrjcwhIaJNBGMBCodN+1cfkwtM1SbUHmvyy35KGT7fohbpOxkE2uLz1o6odKK2Ck/tz47z+VqQfi9Q==", + "requires": { + "babel-plugin-transform-strict-mode": "^6.24.1", + "babel-runtime": "^6.26.0", + "babel-template": "^6.26.0", + "babel-types": "^6.26.0" + } + }, + "babel-plugin-transform-es2015-modules-systemjs": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-systemjs/-/babel-plugin-transform-es2015-modules-systemjs-6.24.1.tgz", + "integrity": "sha1-/4mhQrkRmpBhlfXxBuzzBdlAfSM=", + "requires": { + "babel-helper-hoist-variables": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-modules-umd": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-modules-umd/-/babel-plugin-transform-es2015-modules-umd-6.24.1.tgz", + "integrity": "sha1-rJl+YoXNGO1hdq22B9YCNErThGg=", + "requires": { + "babel-plugin-transform-es2015-modules-amd": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-object-super": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-object-super/-/babel-plugin-transform-es2015-object-super-6.24.1.tgz", + "integrity": "sha1-JM72muIcuDp/hgPa0CH1cusnj40=", + "requires": { + "babel-helper-replace-supers": "^6.24.1", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-parameters": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-parameters/-/babel-plugin-transform-es2015-parameters-6.24.1.tgz", + "integrity": "sha1-V6w1GrScrxSpfNE7CfZv3wpiXys=", + "requires": { + "babel-helper-call-delegate": "^6.24.1", + "babel-helper-get-function-arity": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-template": "^6.24.1", + "babel-traverse": "^6.24.1", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-shorthand-properties": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-shorthand-properties/-/babel-plugin-transform-es2015-shorthand-properties-6.24.1.tgz", + "integrity": "sha1-JPh11nIch2YbvZmkYi5R8U3jiqA=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-spread": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-spread/-/babel-plugin-transform-es2015-spread-6.22.0.tgz", + "integrity": "sha1-1taKmfia7cRTbIGlQujdnxdG+NE=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-sticky-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-sticky-regex/-/babel-plugin-transform-es2015-sticky-regex-6.24.1.tgz", + "integrity": "sha1-AMHNsaynERLN8M9hJsLta0V8zbw=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-plugin-transform-es2015-template-literals": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-template-literals/-/babel-plugin-transform-es2015-template-literals-6.22.0.tgz", + "integrity": "sha1-qEs0UPfp+PH2g51taH2oS7EjbY0=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-typeof-symbol": { + "version": "6.23.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-typeof-symbol/-/babel-plugin-transform-es2015-typeof-symbol-6.23.0.tgz", + "integrity": "sha1-3sCfHN3/lLUqxz1QXITfWdzOs3I=", + "requires": { + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-es2015-unicode-regex": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-es2015-unicode-regex/-/babel-plugin-transform-es2015-unicode-regex-6.24.1.tgz", + "integrity": "sha1-04sS9C6nMj9yk4fxinxa4frrNek=", + "requires": { + "babel-helper-regex": "^6.24.1", + "babel-runtime": "^6.22.0", + "regexpu-core": "^2.0.0" + } + }, + "babel-plugin-transform-exponentiation-operator": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-exponentiation-operator/-/babel-plugin-transform-exponentiation-operator-6.24.1.tgz", + "integrity": "sha1-KrDJx/MJj6SJB3cruBP+QejeOg4=", + "requires": { + "babel-helper-builder-binary-assignment-operator-visitor": "^6.24.1", + "babel-plugin-syntax-exponentiation-operator": "^6.8.0", + "babel-runtime": "^6.22.0" + } + }, + "babel-plugin-transform-regenerator": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-regenerator/-/babel-plugin-transform-regenerator-6.26.0.tgz", + "integrity": "sha1-4HA2lvveJ/Cj78rPi03KL3s6jy8=", + "requires": { + "regenerator-transform": "^0.10.0" + } + }, + "babel-plugin-transform-strict-mode": { + "version": "6.24.1", + "resolved": "https://registry.npmjs.org/babel-plugin-transform-strict-mode/-/babel-plugin-transform-strict-mode-6.24.1.tgz", + "integrity": "sha1-1fr3qleKZbvlkc9e2uBKDGcCB1g=", + "requires": { + "babel-runtime": "^6.22.0", + "babel-types": "^6.24.1" + } + }, + "babel-preset-env": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/babel-preset-env/-/babel-preset-env-1.7.0.tgz", + "integrity": "sha512-9OR2afuKDneX2/q2EurSftUYM0xGu4O2D9adAhVfADDhrYDaxXV0rBbevVYoY9n6nyX1PmQW/0jtpJvUNr9CHg==", + "requires": { + "babel-plugin-check-es2015-constants": "^6.22.0", + "babel-plugin-syntax-trailing-function-commas": "^6.22.0", + "babel-plugin-transform-async-to-generator": "^6.22.0", + "babel-plugin-transform-es2015-arrow-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoped-functions": "^6.22.0", + "babel-plugin-transform-es2015-block-scoping": "^6.23.0", + "babel-plugin-transform-es2015-classes": "^6.23.0", + "babel-plugin-transform-es2015-computed-properties": "^6.22.0", + "babel-plugin-transform-es2015-destructuring": "^6.23.0", + "babel-plugin-transform-es2015-duplicate-keys": "^6.22.0", + "babel-plugin-transform-es2015-for-of": "^6.23.0", + "babel-plugin-transform-es2015-function-name": "^6.22.0", + "babel-plugin-transform-es2015-literals": "^6.22.0", + "babel-plugin-transform-es2015-modules-amd": "^6.22.0", + "babel-plugin-transform-es2015-modules-commonjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-systemjs": "^6.23.0", + "babel-plugin-transform-es2015-modules-umd": "^6.23.0", + "babel-plugin-transform-es2015-object-super": "^6.22.0", + "babel-plugin-transform-es2015-parameters": "^6.23.0", + "babel-plugin-transform-es2015-shorthand-properties": "^6.22.0", + "babel-plugin-transform-es2015-spread": "^6.22.0", + "babel-plugin-transform-es2015-sticky-regex": "^6.22.0", + "babel-plugin-transform-es2015-template-literals": "^6.22.0", + "babel-plugin-transform-es2015-typeof-symbol": "^6.23.0", + "babel-plugin-transform-es2015-unicode-regex": "^6.22.0", + "babel-plugin-transform-exponentiation-operator": "^6.22.0", + "babel-plugin-transform-regenerator": "^6.22.0", + "browserslist": "^3.2.6", + "invariant": "^2.2.2", + "semver": "^5.3.0" + }, + "dependencies": { + "semver": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz", + "integrity": "sha512-sauaDf/PZdVgrLTNYHRtpXa1iRiKcaebiKQ1BJdpQlWH2lCvexQdX55snPFyK7QzpudqbCI0qXFfOasHdyNDGQ==" + } + } + }, + "babel-register": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-register/-/babel-register-6.26.0.tgz", + "integrity": "sha1-btAhFz4vy0htestFxgCahW9kcHE=", + "requires": { + "babel-core": "^6.26.0", + "babel-runtime": "^6.26.0", + "core-js": "^2.5.0", + "home-or-tmp": "^2.0.0", + "lodash": "^4.17.4", + "mkdirp": "^0.5.1", + "source-map-support": "^0.4.15" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + }, + "source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha1-igOdLRAh0i0eoUyA2OpGi6LvP8w=" + }, + "source-map-support": { + "version": "0.4.18", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.4.18.tgz", + "integrity": "sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==", + "requires": { + "source-map": "^0.5.6" + } + } + } + }, + "babel-runtime": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-runtime/-/babel-runtime-6.26.0.tgz", + "integrity": "sha1-llxwWGaOgrVde/4E/yM3vItWR/4=", + "requires": { + "core-js": "^2.4.0", + "regenerator-runtime": "^0.11.0" + }, + "dependencies": { + "regenerator-runtime": { + "version": "0.11.1", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz", + "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==" + } + } + }, + "babel-template": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-template/-/babel-template-6.26.0.tgz", + "integrity": "sha1-3gPi0WOWsGn0bdn/+FIfsaDjXgI=", + "requires": { + "babel-runtime": "^6.26.0", + "babel-traverse": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "lodash": "^4.17.4" + } + }, + "babel-traverse": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-traverse/-/babel-traverse-6.26.0.tgz", + "integrity": "sha1-RqnL1+3MYsjlwGTi0tjQ9ANXZu4=", + "requires": { + "babel-code-frame": "^6.26.0", + "babel-messages": "^6.23.0", + "babel-runtime": "^6.26.0", + "babel-types": "^6.26.0", + "babylon": "^6.18.0", + "debug": "^2.6.8", + "globals": "^9.18.0", + "invariant": "^2.2.2", + "lodash": "^4.17.4" + } + }, + "babel-types": { + "version": "6.26.0", + "resolved": "https://registry.npmjs.org/babel-types/-/babel-types-6.26.0.tgz", + "integrity": "sha1-o7Bz+Uq0nrb6Vc1lInozQ4BjJJc=", + "requires": { + "babel-runtime": "^6.26.0", + "esutils": "^2.0.2", + "lodash": "^4.17.4", + "to-fast-properties": "^1.0.3" + }, + "dependencies": { + "to-fast-properties": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-1.0.3.tgz", + "integrity": "sha1-uDVx+k2MJbguIxsG46MFXeTKGkc=" + } + } + }, + "babelify": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/babelify/-/babelify-7.3.0.tgz", + "integrity": "sha1-qlau3nBn/XvVSWZu4W3ChQh+iOU=", + "requires": { + "babel-core": "^6.0.14", + "object-assign": "^4.0.0" + } + }, + "babylon": { + "version": "6.18.0", + "resolved": "https://registry.npmjs.org/babylon/-/babylon-6.18.0.tgz", + "integrity": "sha512-q/UEjfGJ2Cm3oKV71DJz9d25TPnq5rhBVL2Q4fA5wcC3jcrdn7+SssEybFIxwAvvP+YCsCYNKughoF33GxgycQ==" + }, + "backoff": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/backoff/-/backoff-2.5.0.tgz", + "integrity": "sha1-9hbtqdPktmuMp/ynn2lXIsX44m8=", + "requires": { + "precond": "0.2" + } + }, + "balanced-match": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz", + "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=" + }, + "base-x": { + "version": "3.0.8", + "resolved": "https://registry.npmjs.org/base-x/-/base-x-3.0.8.tgz", + "integrity": "sha512-Rl/1AWP4J/zRrk54hhlxH4drNxPJXYUaKffODVI53/dAsV4t9fBxyxYKAVPU1XBHxYwOWP9h9H0hM2MVw4YfJA==", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "base32-decode": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/base32-decode/-/base32-decode-1.0.0.tgz", + "integrity": "sha512-KNWUX/R7wKenwE/G/qFMzGScOgVntOmbE27vvc6GrniDGYb6a5+qWcuoXl8WIOQL7q0TpK7nZDm1Y04Yi3Yn5g==" + }, + "base32-encode": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/base32-encode/-/base32-encode-1.1.1.tgz", + "integrity": "sha512-eqa0BeGghj3guezlasdHJhr3+J5ZbbQvxeprkcDMbRQrjlqOT832IUDT4Al4ofAwekFYMqkkM9KMUHs9Cu0HKA==" + }, + "base64-js": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.3.1.tgz", + "integrity": "sha512-mLQ4i2QO1ytvGWFWmcngKO//JXAQueZvwEKtjgQFM4jIK0kU+ytMfplL8j+n5mspOfjHwoAg+9yhb7BwAHm36g==" + }, + "bcrypt-pbkdf": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/bcrypt-pbkdf/-/bcrypt-pbkdf-1.0.2.tgz", + "integrity": "sha1-pDAdOJtqQ/m2f/PKEaP2Y342Dp4=", + "requires": { + "tweetnacl": "^0.14.3" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + } + } + }, + "bindings": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/bindings/-/bindings-1.5.0.tgz", + "integrity": "sha512-p2q/t/mhvuOj/UeLlV6566GD/guowlr0hHxClI0W9m7MWYkL1F0hLo+0Aexs9HSPCtR1SXQ0TD3MMKrXZajbiQ==", + "requires": { + "file-uri-to-path": "1.0.0" + } + }, + "bip39": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-2.6.0.tgz", + "integrity": "sha512-RrnQRG2EgEoqO24ea+Q/fftuPUZLmrEM3qNhhGsA3PbaXaCW791LTzPuVyx/VprXQcTbPJ3K3UeTna8ZnVl2sg==", + "requires": { + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1", + "safe-buffer": "^5.0.1", + "unorm": "^1.3.3" + } + }, + "bl": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/bl/-/bl-1.2.3.tgz", + "integrity": "sha512-pvcNpa0UU69UT341rO6AYy4FVAIkUHuZXRIWbq+zHnsVcRzDDjIAhGuuYoi0d//cwIwtt4pkpKycWEfjdV+vww==", + "requires": { + "readable-stream": "^2.3.5", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + } + } + }, + "blake2b": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/blake2b/-/blake2b-2.1.3.tgz", + "integrity": "sha512-pkDss4xFVbMb4270aCyGD3qLv92314Et+FsKzilCLxDz5DuZ2/1g3w4nmBbu6nKApPspnjG7JcwTjGZnduB1yg==", + "requires": { + "blake2b-wasm": "^1.1.0", + "nanoassert": "^1.0.0" + } + }, + "blake2b-wasm": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/blake2b-wasm/-/blake2b-wasm-1.1.7.tgz", + "integrity": "sha512-oFIHvXhlz/DUgF0kq5B1CqxIDjIJwh9iDeUUGQUcvgiGz7Wdw03McEO7CfLBy7QKGdsydcMCgO9jFNBAFCtFcA==", + "requires": { + "nanoassert": "^1.0.0" + } + }, + "blakejs": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/blakejs/-/blakejs-1.1.0.tgz", + "integrity": "sha1-ad+S75U6qIylGjLfarHFShVfx6U=" + }, + "bluebird": { + "version": "3.7.2", + "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", + "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" + }, + "bn.js": { + "version": "4.11.9", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.9.tgz", + "integrity": "sha512-E6QoYqCKZfgatHTdHzs1RRKP7ip4vvm+EyRUeE2RF0NblwVvb0p6jSVeNTOFxPn26QXN2o6SMfNxKp6kU8zQaw==" + }, + "body-parser": { + "version": "1.19.0", + "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.19.0.tgz", + "integrity": "sha512-dhEPs72UPbDnAQJ9ZKMNTP6ptJaionhP5cBb541nXPlW60Jepo9RV/a4fX4XWW9CuFNK22krhrj1+rgzifNCsw==", + "requires": { + "bytes": "3.1.0", + "content-type": "~1.0.4", + "debug": "2.6.9", + "depd": "~1.1.2", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "on-finished": "~2.3.0", + "qs": "6.7.0", + "raw-body": "2.4.0", + "type-is": "~1.6.17" + } + }, + "brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "requires": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha1-EsJe/kCkXjwyPrhnWgoM5XsiNx8=" + }, + "browserify-aes": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/browserify-aes/-/browserify-aes-1.2.0.tgz", + "integrity": "sha512-+7CHXqGuspUn/Sl5aO7Ea0xWGAtETPXNSAjHo48JfLdPWcMng33Xe4znFvQweqc/uzk5zSOI3H52CYnjCfb5hA==", + "requires": { + "buffer-xor": "^1.0.3", + "cipher-base": "^1.0.0", + "create-hash": "^1.1.0", + "evp_bytestokey": "^1.0.3", + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "browserify-cipher": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/browserify-cipher/-/browserify-cipher-1.0.1.tgz", + "integrity": "sha512-sPhkz0ARKbf4rRQt2hTpAHqn47X3llLkUGn+xEJzLjwY8LRs2p0v7ljvI5EyoRO/mexrNunNECisZs+gw2zz1w==", + "requires": { + "browserify-aes": "^1.0.4", + "browserify-des": "^1.0.0", + "evp_bytestokey": "^1.0.0" + } + }, + "browserify-des": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/browserify-des/-/browserify-des-1.0.2.tgz", + "integrity": "sha512-BioO1xf3hFwz4kc6iBhI3ieDFompMhrMlnDFC4/0/vd5MokpuAc3R+LYbwTA9A5Yc9pq9UYPqffKpW2ObuwX5A==", + "requires": { + "cipher-base": "^1.0.1", + "des.js": "^1.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "browserify-rsa": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/browserify-rsa/-/browserify-rsa-4.0.1.tgz", + "integrity": "sha1-IeCr+vbyApzy+vsTNWenAdQTVSQ=", + "requires": { + "bn.js": "^4.1.0", + "randombytes": "^2.0.1" + } + }, + "browserify-sign": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/browserify-sign/-/browserify-sign-4.2.1.tgz", + "integrity": "sha512-/vrA5fguVAKKAVTNJjgSm1tRQDHUU6DbwO9IROu/0WAzC8PKhucDSh18J0RMvVeHAn5puMd+QHC2erPRNf8lmg==", + "requires": { + "bn.js": "^5.1.1", + "browserify-rsa": "^4.0.1", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "elliptic": "^6.5.3", + "inherits": "^2.0.4", + "parse-asn1": "^5.1.5", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + }, + "dependencies": { + "bn.js": { + "version": "5.1.3", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.1.3.tgz", + "integrity": "sha512-GkTiFpjFtUzU9CbMeJ5iazkCzGL3jrhzerzZIuqLABjbwRaFt33I9tUdSNryIptM+RxDet6OKm2WnLXzW51KsQ==" + } + } + }, + "browserslist": { + "version": "3.2.8", + "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-3.2.8.tgz", + "integrity": "sha512-WHVocJYavUwVgVViC0ORikPHQquXwVh939TaelZ4WDqpWgTX/FsGhl/+P4qBUAGcRvtOgDgC+xftNWWp2RUTAQ==", + "requires": { + "caniuse-lite": "^1.0.30000844", + "electron-to-chromium": "^1.3.47" + } + }, + "bs58": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/bs58/-/bs58-4.0.1.tgz", + "integrity": "sha1-vhYedsNU9veIrkBx9j806MTwpCo=", + "requires": { + "base-x": "^3.0.2" + } + }, + "bs58check": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/bs58check/-/bs58check-2.1.2.tgz", + "integrity": "sha512-0TS1jicxdU09dwJMNZtVAfzPi6Q6QeN0pM1Fkzrjn+XYHvzMKPU3pHVpva+769iNVSfIYWf7LJ6WR+BuuMf8cA==", + "requires": { + "bs58": "^4.0.0", + "create-hash": "^1.1.0", + "safe-buffer": "^5.1.2" + } + }, + "btoa": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/btoa/-/btoa-1.2.1.tgz", + "integrity": "sha512-SB4/MIGlsiVkMcHmT+pSmIPoNDoHg+7cMzmt3Uxt628MTz2487DKSqK/fuhFBrkuqrYv5UCEnACpF4dTFNKc/g==" + }, + "buffer": { + "version": "5.6.0", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.6.0.tgz", + "integrity": "sha512-/gDYp/UtU0eA1ys8bOs9J6a+E/KWIY+DZ+Q2WESNUA0jFRsJOc0SNUO6xJ5SGA1xueg3NL65W6s+NY5l9cunuw==", + "requires": { + "base64-js": "^1.0.2", + "ieee754": "^1.1.4" + } + }, + "buffer-alloc": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/buffer-alloc/-/buffer-alloc-1.2.0.tgz", + "integrity": "sha512-CFsHQgjtW1UChdXgbyJGtnm+O/uLQeZdtbDo8mfUgYXCHSM1wgrVxXm6bSyrUuErEb+4sYVGCzASBRot7zyrow==", + "requires": { + "buffer-alloc-unsafe": "^1.1.0", + "buffer-fill": "^1.0.0" + } + }, + "buffer-alloc-unsafe": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/buffer-alloc-unsafe/-/buffer-alloc-unsafe-1.1.0.tgz", + "integrity": "sha512-TEM2iMIEQdJ2yjPJoSIsldnleVaAk1oW3DBVUykyOLsEsFmEc9kn+SFFPz+gl54KQNxlDnAwCXosOS9Okx2xAg==" + }, + "buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha1-DTM+PwDqxQqhRUq9MO+MKl2ackI=" + }, + "buffer-fill": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/buffer-fill/-/buffer-fill-1.0.0.tgz", + "integrity": "sha1-+PeLdniYiO858gXNY39o5wISKyw=" + }, + "buffer-from": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", + "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==" + }, + "buffer-to-arraybuffer": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/buffer-to-arraybuffer/-/buffer-to-arraybuffer-0.0.5.tgz", + "integrity": "sha1-YGSkD6dutDxyOrqe+PbhIW0QURo=" + }, + "buffer-xor": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/buffer-xor/-/buffer-xor-1.0.3.tgz", + "integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=" + }, + "bytes": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/bytes/-/bytes-3.1.0.tgz", + "integrity": "sha512-zauLjrfCG+xvoyaqLoV8bLVXXNGC4JqlxFCutSDWA6fJrTo2ZuvLYTqZ7aHBLZSMOopbzwv8f+wZcVzfVTI2Dg==" + }, + "cacheable-request": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/cacheable-request/-/cacheable-request-6.1.0.tgz", + "integrity": "sha512-Oj3cAGPCqOZX7Rz64Uny2GYAZNliQSqfbePrgAQ1wKAihYmCUnraBtJtKcGR4xz7wF+LoJC+ssFZvv5BgF9Igg==", + "requires": { + "clone-response": "^1.0.2", + "get-stream": "^5.1.0", + "http-cache-semantics": "^4.0.0", + "keyv": "^3.0.0", + "lowercase-keys": "^2.0.0", + "normalize-url": "^4.1.0", + "responselike": "^1.0.2" + }, + "dependencies": { + "get-stream": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-5.2.0.tgz", + "integrity": "sha512-nBF+F1rAZVCu/p7rjzgA+Yb4lfYXrpl7a6VmJrU8wF9I1CKvP/QwPNZHnOlwbTkY6dvtFIzFMSyQXbLoTQPRpA==", + "requires": { + "pump": "^3.0.0" + } + }, + "lowercase-keys": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-2.0.0.tgz", + "integrity": "sha512-tqNXrS78oMOE73NMxK4EMLQsQowWf8jKooH9g7xPavRT706R6bkQJ6DY2Te7QukaZsulxa30wQ7bk0pm4XiHmA==" + } + } + }, + "caniuse-lite": { + "version": "1.0.30001124", + "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001124.tgz", + "integrity": "sha512-zQW8V3CdND7GHRH6rxm6s59Ww4g/qGWTheoboW9nfeMg7sUoopIfKCcNZUjwYRCOrvereh3kwDpZj4VLQ7zGtA==" + }, + "caseless": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/caseless/-/caseless-0.12.0.tgz", + "integrity": "sha1-G2gcIf+EAzyCZUMJBolCDRhxUdw=" + }, + "chalk": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-1.1.3.tgz", + "integrity": "sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=", + "requires": { + "ansi-styles": "^2.2.1", + "escape-string-regexp": "^1.0.2", + "has-ansi": "^2.0.0", + "strip-ansi": "^3.0.0", + "supports-color": "^2.0.0" + } + }, + "checkpoint-store": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/checkpoint-store/-/checkpoint-store-1.1.0.tgz", + "integrity": "sha1-BOTLUWuRQziTWB5tRgGnjpVS6gY=", + "requires": { + "functional-red-black-tree": "^1.0.1" + } + }, + "chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==" + }, + "cipher-base": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/cipher-base/-/cipher-base-1.0.4.tgz", + "integrity": "sha512-Kkht5ye6ZGmwv40uUDZztayT2ThLQGfnj/T71N/XzeZeo3nf8foyW7zGTsPYkEya3m5f3cAypH+qe7YOrM1U2Q==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha1-G39Ln1kfHo+DZwQBYANFoCiHQ18=" + }, + "clone-response": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/clone-response/-/clone-response-1.0.2.tgz", + "integrity": "sha1-0dyXOSAxTfZ/vrlCI7TuNQI56Ws=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "requires": { + "delayed-stream": "~1.0.0" + } + }, + "commander": { + "version": "2.20.3", + "resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz", + "integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==" + }, + "concat-map": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", + "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=" + }, + "concat-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/concat-stream/-/concat-stream-1.6.2.tgz", + "integrity": "sha512-27HBghJxjiZtIk3Ycvn/4kbJk/1uZuJFfuPEns6LaEvpvG1f0hTea8lilrouyo9mVc2GWdcEZ8OLoGmSADlrCw==", + "requires": { + "buffer-from": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^2.2.2", + "typedarray": "^0.0.6" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "content-disposition": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/content-disposition/-/content-disposition-0.5.3.tgz", + "integrity": "sha512-ExO0774ikEObIAEV9kDo50o+79VCUdEB6n6lzKgGwupcVeRlhrj3qGAfwq8G6uBJjkqLrhT0qEYFcWng8z1z0g==", + "requires": { + "safe-buffer": "5.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "content-type": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/content-type/-/content-type-1.0.4.tgz", + "integrity": "sha512-hIP3EEPs8tB9AT1L+NUqtwOAps4mk2Zob89MWXMHjHWg9milF/j4osnnQLXBCBFBk/tvIG/tUc9mOUJiPBhPXA==" + }, + "convert-source-map": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-1.7.0.tgz", + "integrity": "sha512-4FJkXzKXEDB1snCFZlLP4gpC3JILicCpGbzG9f9G7tGqGCzETQ2hWPrcinA9oU4wtf2biUaEH5065UnMeR33oA==", + "requires": { + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "cookie": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.0.tgz", + "integrity": "sha512-+Hp8fLp57wnUSt0tY0tHEXh4voZRDnoIrZPqlo3DPiI4y9lwg/jqx+1Om94/W6ZaPDOUbnjOt/99w66zk+l1Xg==" + }, + "cookie-signature": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/cookie-signature/-/cookie-signature-1.0.6.tgz", + "integrity": "sha1-4wOogrNCzD7oylE6eZmXNNqzriw=" + }, + "cookiejar": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/cookiejar/-/cookiejar-2.1.2.tgz", + "integrity": "sha512-Mw+adcfzPxcPeI+0WlvRrr/3lGVO0bD75SxX6811cxSh1Wbxx7xZBGK1eVtDf6si8rg2lhnUjsVLMFMfbRIuwA==" + }, + "core-js": { + "version": "2.6.11", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-2.6.11.tgz", + "integrity": "sha512-5wjnpaT/3dV+XB4borEsnAYQchn00XSgTAWKDkEqv+K8KevjbzmofK6hfJ9TZIlpj2N0xQpazy7PiRQiWHqzWg==" + }, + "core-util-is": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.2.tgz", + "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=" + }, + "cors": { + "version": "2.8.5", + "resolved": "https://registry.npmjs.org/cors/-/cors-2.8.5.tgz", + "integrity": "sha512-KIHbLJqu73RGr/hnbrO9uBeixNGuvSQjul/jdFvS/KFSIH1hWVd1ng7zOHx+YrEfInLG7q4n6GHQ9cDtxv/P6g==", + "requires": { + "object-assign": "^4", + "vary": "^1" + } + }, + "create-ecdh": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/create-ecdh/-/create-ecdh-4.0.4.tgz", + "integrity": "sha512-mf+TCx8wWc9VpuxfP2ht0iSISLZnt0JgWlrOKZiNqyUZWnjIaCIVNQArMHnCZKfEYRg6IM7A+NeJoN8gf/Ws0A==", + "requires": { + "bn.js": "^4.1.0", + "elliptic": "^6.5.3" + } + }, + "create-hash": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/create-hash/-/create-hash-1.2.0.tgz", + "integrity": "sha512-z00bCGNHDG8mHAkP7CtT1qVu+bFQUPjYq/4Iv3C3kWjTFV10zIjfSoeqXo9Asws8gwSHDGj/hl2u4OGIjapeCg==", + "requires": { + "cipher-base": "^1.0.1", + "inherits": "^2.0.1", + "md5.js": "^1.3.4", + "ripemd160": "^2.0.1", + "sha.js": "^2.4.0" + } + }, + "create-hmac": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/create-hmac/-/create-hmac-1.1.7.tgz", + "integrity": "sha512-MJG9liiZ+ogc4TzUwuvbER1JRdgvUFSB5+VR/g5h82fGaIRWMWddtKBHi7/sVhfjQZ6SehlyhvQYrcYkaUIpLg==", + "requires": { + "cipher-base": "^1.0.3", + "create-hash": "^1.1.0", + "inherits": "^2.0.1", + "ripemd160": "^2.0.0", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "cross-fetch": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.3.tgz", + "integrity": "sha512-PrWWNH3yL2NYIb/7WF/5vFG3DCQiXDOVf8k3ijatbrtnwNuhMWLC7YF7uqf53tbTFDzHIUD8oITw4Bxt8ST3Nw==", + "requires": { + "node-fetch": "2.1.2", + "whatwg-fetch": "2.0.4" + } + }, + "crypto-browserify": { + "version": "3.12.0", + "resolved": "https://registry.npmjs.org/crypto-browserify/-/crypto-browserify-3.12.0.tgz", + "integrity": "sha512-fz4spIh+znjO2VjL+IdhEpRJ3YN6sMzITSBijk6FK2UvTqruSQW+/cCZTSNsMiZNvUeq0CqurF+dAbyiGOY6Wg==", + "requires": { + "browserify-cipher": "^1.0.0", + "browserify-sign": "^4.0.0", + "create-ecdh": "^4.0.0", + "create-hash": "^1.1.0", + "create-hmac": "^1.1.0", + "diffie-hellman": "^5.0.0", + "inherits": "^2.0.1", + "pbkdf2": "^3.0.3", + "public-encrypt": "^4.0.0", + "randombytes": "^2.0.0", + "randomfill": "^1.0.3" + } + }, + "d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "requires": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, + "dashdash": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/dashdash/-/dashdash-1.14.1.tgz", + "integrity": "sha1-hTz6D3y+L+1d4gMmuN1YEDX24vA=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "requires": { + "ms": "2.0.0" + } + }, + "decode-uri-component": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", + "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + }, + "decompress": { + "version": "4.2.1", + "resolved": "https://registry.npmjs.org/decompress/-/decompress-4.2.1.tgz", + "integrity": "sha512-e48kc2IjU+2Zw8cTb6VZcJQ3lgVbS4uuB1TfCHbiZIP/haNXm+SVyhu+87jts5/3ROpd82GSVCoNs/z8l4ZOaQ==", + "requires": { + "decompress-tar": "^4.0.0", + "decompress-tarbz2": "^4.0.0", + "decompress-targz": "^4.0.0", + "decompress-unzip": "^4.0.1", + "graceful-fs": "^4.1.10", + "make-dir": "^1.0.0", + "pify": "^2.3.0", + "strip-dirs": "^2.0.0" + } + }, + "decompress-response": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-3.3.0.tgz", + "integrity": "sha1-gKTdMjdIOEv6JICDYirt7Jgq3/M=", + "requires": { + "mimic-response": "^1.0.0" + } + }, + "decompress-tar": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tar/-/decompress-tar-4.1.1.tgz", + "integrity": "sha512-JdJMaCrGpB5fESVyxwpCx4Jdj2AagLmv3y58Qy4GE6HMVjWz1FeVQk1Ct4Kye7PftcdOo/7U7UKzYBJgqnGeUQ==", + "requires": { + "file-type": "^5.2.0", + "is-stream": "^1.1.0", + "tar-stream": "^1.5.2" + } + }, + "decompress-tarbz2": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-tarbz2/-/decompress-tarbz2-4.1.1.tgz", + "integrity": "sha512-s88xLzf1r81ICXLAVQVzaN6ZmX4A6U4z2nMbOwobxkLoIIfjVMBg7TeguTUXkKeXni795B6y5rnvDw7rxhAq9A==", + "requires": { + "decompress-tar": "^4.1.0", + "file-type": "^6.1.0", + "is-stream": "^1.1.0", + "seek-bzip": "^1.0.5", + "unbzip2-stream": "^1.0.9" + }, + "dependencies": { + "file-type": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-6.2.0.tgz", + "integrity": "sha512-YPcTBDV+2Tm0VqjybVd32MHdlEGAtuxS3VAYsumFokDSMG+ROT5wawGlnHDoz7bfMcMDt9hxuXvXwoKUx2fkOg==" + } + } + }, + "decompress-targz": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/decompress-targz/-/decompress-targz-4.1.1.tgz", + "integrity": "sha512-4z81Znfr6chWnRDNfFNqLwPvm4db3WuZkqV+UgXQzSngG3CEKdBkw5jrv3axjjL96glyiiKjsxJG3X6WBZwX3w==", + "requires": { + "decompress-tar": "^4.1.1", + "file-type": "^5.2.0", + "is-stream": "^1.1.0" + } + }, + "decompress-unzip": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/decompress-unzip/-/decompress-unzip-4.0.1.tgz", + "integrity": "sha1-3qrM39FK6vhVePczroIQ+bSEj2k=", + "requires": { + "file-type": "^3.8.0", + "get-stream": "^2.2.0", + "pify": "^2.3.0", + "yauzl": "^2.4.2" + }, + "dependencies": { + "file-type": { + "version": "3.9.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-3.9.0.tgz", + "integrity": "sha1-JXoHg4TR24CHvESdEH1SpSZyuek=" + }, + "get-stream": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-2.3.1.tgz", + "integrity": "sha1-Xzj5PzRgCWZu4BUKBUFn+Rvdld4=", + "requires": { + "object-assign": "^4.0.1", + "pinkie-promise": "^2.0.0" + } + } + } + }, + "deep-equal": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/deep-equal/-/deep-equal-1.1.1.tgz", + "integrity": "sha512-yd9c5AdiqVcR+JjcwUQb9DkhJc8ngNr0MahEBGvDiJw8puWab2yZlh+nkasOnZP+EGTAP6rRp2JzJhJZzvNF8g==", + "requires": { + "is-arguments": "^1.0.4", + "is-date-object": "^1.0.1", + "is-regex": "^1.0.4", + "object-is": "^1.0.1", + "object-keys": "^1.1.1", + "regexp.prototype.flags": "^1.2.0" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, + "defer-to-connect": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/defer-to-connect/-/defer-to-connect-1.1.3.tgz", + "integrity": "sha512-0ISdNousHvZT2EiFlZeZAHBUvSxmKswVCEf8hW7KWgG4a8MVEu/3Vb6uWYozkjylyCxe0JBIiRB1jV45S70WVQ==" + }, + "deferred-leveldown": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/deferred-leveldown/-/deferred-leveldown-1.2.2.tgz", + "integrity": "sha512-uukrWD2bguRtXilKt6cAWKyoXrTSMo5m7crUdLfWQmu8kIm88w3QZoUL+6nhpfKVmhHANER6Re3sKoNoZ3IKMA==", + "requires": { + "abstract-leveldown": "~2.6.0" + } + }, + "define-properties": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz", + "integrity": "sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==", + "requires": { + "object-keys": "^1.0.12" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, + "defined": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/defined/-/defined-1.0.0.tgz", + "integrity": "sha1-yY2bzvdWdBiOEQlpFRGZ45sfppM=" + }, + "delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha1-3zrhmayt+31ECqrgsp4icrJOxhk=" + }, + "depd": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/depd/-/depd-1.1.2.tgz", + "integrity": "sha1-m81S4UwJd2PnSbJ0xDRu0uVgtak=" + }, + "des.js": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/des.js/-/des.js-1.0.1.tgz", + "integrity": "sha512-Q0I4pfFrv2VPd34/vfLrFOoRmlYj3OV50i7fskps1jZWK1kApMWWT9G6RRUeYedLcBDIhnSDaUvJMb3AhUlaEA==", + "requires": { + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0" + } + }, + "destroy": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/destroy/-/destroy-1.0.4.tgz", + "integrity": "sha1-l4hXRCxEdJ5CBmE+N5RiBYJqvYA=" + }, + "detect-indent": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/detect-indent/-/detect-indent-4.0.0.tgz", + "integrity": "sha1-920GQ1LN9Docts5hnE7jqUdd4gg=", + "requires": { + "repeating": "^2.0.0" + } + }, + "diffie-hellman": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/diffie-hellman/-/diffie-hellman-5.0.3.tgz", + "integrity": "sha512-kqag/Nl+f3GwyK25fhUMYj81BUOrZ9IuJsjIcDE5icNM9FJHAVm3VcUDxdLPoQtTuUylWm6ZIknYJwwaPxsUzg==", + "requires": { + "bn.js": "^4.1.0", + "miller-rabin": "^4.0.0", + "randombytes": "^2.0.0" + } + }, + "dom-walk": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dom-walk/-/dom-walk-0.1.2.tgz", + "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w==" + }, + "dotignore": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/dotignore/-/dotignore-0.1.2.tgz", + "integrity": "sha512-UGGGWfSauusaVJC+8fgV+NVvBXkCTmVv7sk6nojDZZvuOUNGUy0Zk4UpHQD6EDjS0jpBwcACvH4eofvyzBcRDw==", + "requires": { + "minimatch": "^3.0.4" + } + }, + "duplexer3": { + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/duplexer3/-/duplexer3-0.1.4.tgz", + "integrity": "sha1-7gHdHKwO08vH/b6jfcCo8c4ALOI=" + }, + "ecc-jsbn": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/ecc-jsbn/-/ecc-jsbn-0.1.2.tgz", + "integrity": "sha1-OoOpBOVDUyh4dMVkt1SThoSamMk=", + "requires": { + "jsbn": "~0.1.0", + "safer-buffer": "^2.1.0" + } + }, + "ed25519-hd-key": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/ed25519-hd-key/-/ed25519-hd-key-1.1.2.tgz", + "integrity": "sha512-/0y9y6N7vM6Kj5ASr9J9wcMVDTtygxSOvYX+PJiMD7VcxCx2G03V5bLRl8Dug9EgkLFsLhGqBtQWQRcElEeWTA==", + "requires": { + "bip39": "3.0.2", + "create-hmac": "1.1.7", + "tweetnacl": "1.0.3" + }, + "dependencies": { + "@types/node": { + "version": "11.11.6", + "resolved": "https://registry.npmjs.org/@types/node/-/node-11.11.6.tgz", + "integrity": "sha512-Exw4yUWMBXM3X+8oqzJNRqZSwUAaS4+7NdvHqQuFi/d+synz++xmX3QIf+BFqneW8N31R8Ky+sikfZUXq07ggQ==" + }, + "bip39": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/bip39/-/bip39-3.0.2.tgz", + "integrity": "sha512-J4E1r2N0tUylTKt07ibXvhpT2c5pyAFgvuA5q1H9uDy6dEGpjV8jmymh3MTYJDLCNbIVClSB9FbND49I6N24MQ==", + "requires": { + "@types/node": "11.11.6", + "create-hash": "^1.1.0", + "pbkdf2": "^3.0.9", + "randombytes": "^2.0.1" + } + } + } + }, + "ee-first": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ee-first/-/ee-first-1.1.1.tgz", + "integrity": "sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=" + }, + "electron-to-chromium": { + "version": "1.3.562", + "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.562.tgz", + "integrity": "sha512-WhRe6liQ2q/w1MZc8mD8INkenHivuHdrr4r5EQHNomy3NJux+incP6M6lDMd0paShP3MD0WGe5R1TWmEClf+Bg==" + }, + "elliptic": { + "version": "6.5.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.3.tgz", + "integrity": "sha512-IMqzv5wNQf+E6aHeIqATs0tOLeOTwj1QKbRcS3jBbYkl5oLAserA8yJTT7/VyHUYG91PRmPyeQDObKLPpeS4dw==", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.0", + "inherits": "^2.0.1", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.0" + } + }, + "encodeurl": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-1.0.2.tgz", + "integrity": "sha1-rT/0yG7C0CkyL1oCw6mmBslbP1k=" + }, + "encoding": { + "version": "0.1.13", + "resolved": "https://registry.npmjs.org/encoding/-/encoding-0.1.13.tgz", + "integrity": "sha512-ETBauow1T35Y/WZMkio9jiM0Z5xjHHmJ4XmjZOq1l/dXz3lr2sRn87nJy20RupqSh1F2m3HHPSp8ShIPQJrJ3A==", + "requires": { + "iconv-lite": "^0.6.2" + }, + "dependencies": { + "iconv-lite": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.2.tgz", + "integrity": "sha512-2y91h5OpQlolefMPmUlivelittSWy0rP+oYVpn6A7GwVHNE8AWzoYOBNmlwks3LobaJxgHCYZAnyNo2GgpNRNQ==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + } + } + } + }, + "end-of-stream": { + "version": "1.4.4", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.4.tgz", + "integrity": "sha512-+uw1inIHVPQoaVuHzRyXd21icM+cnt4CzD5rW+NC1wjOUSTOs+Te7FOv7AhN7vS9x/oIyhLP5PR1H+phQAHu5Q==", + "requires": { + "once": "^1.4.0" + } + }, + "errno": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/errno/-/errno-0.1.7.tgz", + "integrity": "sha512-MfrRBDWzIWifgq6tJj60gkAwtLNb6sQPlcFrSOflcP1aFmmruKQ2wRnze/8V6kgyz7H3FF8Npzv78mZ7XLLflg==", + "requires": { + "prr": "~1.0.1" + } + }, + "es-abstract": { + "version": "1.17.6", + "resolved": "https://registry.npmjs.org/es-abstract/-/es-abstract-1.17.6.tgz", + "integrity": "sha512-Fr89bON3WFyUi5EvAeI48QTWX0AyekGgLA8H+c+7fbfCkJwRWRMLd8CQedNEyJuoYYhmtEqY92pgte1FAhBlhw==", + "requires": { + "es-to-primitive": "^1.2.1", + "function-bind": "^1.1.1", + "has": "^1.0.3", + "has-symbols": "^1.0.1", + "is-callable": "^1.2.0", + "is-regex": "^1.1.0", + "object-inspect": "^1.7.0", + "object-keys": "^1.1.1", + "object.assign": "^4.1.0", + "string.prototype.trimend": "^1.0.1", + "string.prototype.trimstart": "^1.0.1" + }, + "dependencies": { + "is-regex": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.1.tgz", + "integrity": "sha512-1+QkEcxiLlB7VEyFtyBg94e08OAsvq7FUBgApTq/w2ymCLyKJgDPsybBENVtA7XCQEgEXxKPonG+mvYRxh/LIg==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, + "es-to-primitive": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/es-to-primitive/-/es-to-primitive-1.2.1.tgz", + "integrity": "sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==", + "requires": { + "is-callable": "^1.1.4", + "is-date-object": "^1.0.1", + "is-symbol": "^1.0.2" + } + }, + "es5-ext": { + "version": "0.10.53", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz", + "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==", + "requires": { + "es6-iterator": "~2.0.3", + "es6-symbol": "~3.1.3", + "next-tick": "~1.0.0" + } + }, + "es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=", + "requires": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, + "es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "requires": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, + "escape-html": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/escape-html/-/escape-html-1.0.3.tgz", + "integrity": "sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=" + }, + "escape-string-regexp": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", + "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=" + }, + "esutils": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", + "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==" + }, + "etag": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/etag/-/etag-1.8.1.tgz", + "integrity": "sha1-Qa4u62XvpiJorr/qg6x9eSmbCIc=" + }, + "eth-block-tracker": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/eth-block-tracker/-/eth-block-tracker-4.4.3.tgz", + "integrity": "sha512-A8tG4Z4iNg4mw5tP1Vung9N9IjgMNqpiMoJ/FouSFwNCGHv2X0mmOYwtQOJzki6XN7r7Tyo01S29p7b224I4jw==", + "requires": { + "@babel/plugin-transform-runtime": "^7.5.5", + "@babel/runtime": "^7.5.5", + "eth-query": "^2.1.0", + "json-rpc-random-id": "^1.0.1", + "pify": "^3.0.0", + "safe-event-emitter": "^1.0.1" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "eth-ens-namehash": { + "version": "2.0.8", + "resolved": "https://registry.npmjs.org/eth-ens-namehash/-/eth-ens-namehash-2.0.8.tgz", + "integrity": "sha1-IprEbsqG1S4MmR58sq74P/D2i88=", + "requires": { + "idna-uts46-hx": "^2.3.1", + "js-sha3": "^0.5.7" + } + }, + "eth-json-rpc-errors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/eth-json-rpc-errors/-/eth-json-rpc-errors-1.1.1.tgz", + "integrity": "sha512-WT5shJ5KfNqHi9jOZD+ID8I1kuYWNrigtZat7GOQkvwo99f8SzAVaEcWhJUv656WiZOAg3P1RiJQANtUmDmbIg==", + "requires": { + "fast-safe-stringify": "^2.0.6" + } + }, + "eth-json-rpc-filters": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/eth-json-rpc-filters/-/eth-json-rpc-filters-4.1.1.tgz", + "integrity": "sha512-GkXb2h6STznD+AmMzblwXgm1JMvjdK9PTIXG7BvIkTlXQ9g0QOxuU1iQRYHoslF9S30BYBSoLSisAYPdLggW+A==", + "requires": { + "await-semaphore": "^0.1.3", + "eth-json-rpc-middleware": "^4.1.4", + "eth-query": "^2.1.2", + "json-rpc-engine": "^5.1.3", + "lodash.flatmap": "^4.5.0", + "safe-event-emitter": "^1.0.1" + } + }, + "eth-json-rpc-infura": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/eth-json-rpc-infura/-/eth-json-rpc-infura-3.2.1.tgz", + "integrity": "sha512-W7zR4DZvyTn23Bxc0EWsq4XGDdD63+XPUCEhV2zQvQGavDVC4ZpFDK4k99qN7bd7/fjj37+rxmuBOBeIqCA5Mw==", + "requires": { + "cross-fetch": "^2.1.1", + "eth-json-rpc-middleware": "^1.5.0", + "json-rpc-engine": "^3.4.0", + "json-rpc-error": "^2.0.0" + }, + "dependencies": { + "eth-json-rpc-middleware": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-1.6.0.tgz", + "integrity": "sha512-tDVCTlrUvdqHKqivYMjtFZsdD7TtpNLBCfKAcOpaVs7orBMS/A8HWro6dIzNtTZIR05FAbJ3bioFOnZpuCew9Q==", + "requires": { + "async": "^2.5.0", + "eth-query": "^2.1.2", + "eth-tx-summary": "^3.1.2", + "ethereumjs-block": "^1.6.0", + "ethereumjs-tx": "^1.3.3", + "ethereumjs-util": "^5.1.2", + "ethereumjs-vm": "^2.1.0", + "fetch-ponyfill": "^4.0.0", + "json-rpc-engine": "^3.6.0", + "json-rpc-error": "^2.0.0", + "json-stable-stringify": "^1.0.1", + "promise-to-callback": "^1.0.0", + "tape": "^4.6.3" + } + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "json-rpc-engine": { + "version": "3.8.0", + "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-3.8.0.tgz", + "integrity": "sha512-6QNcvm2gFuuK4TKU1uwfH0Qd/cOSb9c1lls0gbnIhciktIUQJwz6NQNAW4B1KiGPenv7IKu97V222Yo1bNhGuA==", + "requires": { + "async": "^2.0.1", + "babel-preset-env": "^1.7.0", + "babelify": "^7.3.0", + "json-rpc-error": "^2.0.0", + "promise-to-callback": "^1.0.0", + "safe-event-emitter": "^1.0.1" + } + } + } + }, + "eth-json-rpc-middleware": { + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/eth-json-rpc-middleware/-/eth-json-rpc-middleware-4.4.1.tgz", + "integrity": "sha512-yoSuRgEYYGFdVeZg3poWOwAlRI+MoBIltmOB86MtpoZjvLbou9EB/qWMOWSmH2ryCWLW97VYY6NWsmWm3OAA7A==", + "requires": { + "btoa": "^1.2.1", + "clone": "^2.1.1", + "eth-json-rpc-errors": "^1.0.1", + "eth-query": "^2.1.2", + "eth-sig-util": "^1.4.2", + "ethereumjs-block": "^1.6.0", + "ethereumjs-tx": "^1.3.7", + "ethereumjs-util": "^5.1.2", + "ethereumjs-vm": "^2.6.0", + "fetch-ponyfill": "^4.0.0", + "json-rpc-engine": "^5.1.3", + "json-stable-stringify": "^1.0.1", + "pify": "^3.0.0", + "safe-event-emitter": "^1.0.1" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "eth-lib": { + "version": "0.1.29", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.1.29.tgz", + "integrity": "sha512-bfttrr3/7gG4E02HoWTDUcDDslN003OlOoBxk9virpAZQ1ja/jDgwkWB8QfJF7ojuEowrqy+lzp9VcJG7/k5bQ==", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "nano-json-stream-parser": "^0.1.2", + "servify": "^0.1.12", + "ws": "^3.0.0", + "xhr-request-promise": "^0.1.2" + } + }, + "eth-query": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/eth-query/-/eth-query-2.1.2.tgz", + "integrity": "sha1-1nQdkAAQa1FRDHLbktY2VFam2l4=", + "requires": { + "json-rpc-random-id": "^1.0.0", + "xtend": "^4.0.1" + } + }, + "eth-rpc-errors": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/eth-rpc-errors/-/eth-rpc-errors-3.0.0.tgz", + "integrity": "sha512-iPPNHPrLwUlR9xCSYm7HHQjWBasor3+KZfRvwEWxMz3ca0yqnlBeJrnyphkGIXZ4J7AMAaOLmwy4AWhnxOiLxg==", + "requires": { + "fast-safe-stringify": "^2.0.6" + } + }, + "eth-sig-util": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/eth-sig-util/-/eth-sig-util-1.4.2.tgz", + "integrity": "sha1-jZWCAsftuq6Dlwf7pvCf8ydgYhA=", + "requires": { + "ethereumjs-abi": "git+https://github.com/ethereumjs/ethereumjs-abi.git", + "ethereumjs-util": "^5.1.1" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "eth-tx-summary": { + "version": "3.2.4", + "resolved": "https://registry.npmjs.org/eth-tx-summary/-/eth-tx-summary-3.2.4.tgz", + "integrity": "sha512-NtlDnaVZah146Rm8HMRUNMgIwG/ED4jiqk0TME9zFheMl1jOp6jL1m0NKGjJwehXQ6ZKCPr16MTr+qspKpEXNg==", + "requires": { + "async": "^2.1.2", + "clone": "^2.0.0", + "concat-stream": "^1.5.1", + "end-of-stream": "^1.1.0", + "eth-query": "^2.0.2", + "ethereumjs-block": "^1.4.1", + "ethereumjs-tx": "^1.1.1", + "ethereumjs-util": "^5.0.1", + "ethereumjs-vm": "^2.6.0", + "through2": "^2.0.3" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereum-common": { + "version": "0.0.18", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.0.18.tgz", + "integrity": "sha1-L9w1dvIykDNYl26znaeDIT/5Uj8=" + }, + "ethereum-cryptography": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/ethereum-cryptography/-/ethereum-cryptography-0.1.3.tgz", + "integrity": "sha512-w8/4x1SGGzc+tO97TASLja6SLd3fRIK2tLVcV2Gx4IB21hE19atll5Cq9o3d0ZmAYC/8aw0ipieTSiekAea4SQ==", + "requires": { + "@types/pbkdf2": "^3.0.0", + "@types/secp256k1": "^4.0.1", + "blakejs": "^1.1.0", + "browserify-aes": "^1.2.0", + "bs58check": "^2.1.2", + "create-hash": "^1.2.0", + "create-hmac": "^1.1.7", + "hash.js": "^1.1.7", + "keccak": "^3.0.0", + "pbkdf2": "^3.0.17", + "randombytes": "^2.1.0", + "safe-buffer": "^5.1.2", + "scrypt-js": "^3.0.0", + "secp256k1": "^4.0.1", + "setimmediate": "^1.0.5" + } + }, + "ethereum-protocol": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/ethereum-protocol/-/ethereum-protocol-1.0.1.tgz", + "integrity": "sha512-3KLX1mHuEsBW0dKG+c6EOJS1NBNqdCICvZW9sInmZTt5aY0oxmHVggYRE0lJu1tcnMD1K+AKHdLi6U43Awm1Vg==" + }, + "ethereumjs-abi": { + "version": "git+https://github.com/ethereumjs/ethereumjs-abi.git#1ce6a1d64235fabe2aaf827fd606def55693508f", + "from": "git+https://github.com/ethereumjs/ethereumjs-abi.git", + "requires": { + "bn.js": "^4.11.8", + "ethereumjs-util": "^6.0.0" + } + }, + "ethereumjs-account": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/ethereumjs-account/-/ethereumjs-account-2.0.5.tgz", + "integrity": "sha512-bgDojnXGjhMwo6eXQC0bY6UK2liSFUSMwwylOmQvZbSl/D7NXQ3+vrGO46ZeOgjGfxXmgIeVNDIiHw7fNZM4VA==", + "requires": { + "ethereumjs-util": "^5.0.0", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-block": { + "version": "1.7.1", + "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-1.7.1.tgz", + "integrity": "sha512-B+sSdtqm78fmKkBq78/QLKJbu/4Ts4P2KFISdgcuZUPDm9x+N7qgBPIIFUGbaakQh8bzuquiRVbdmvPKqbILRg==", + "requires": { + "async": "^2.0.1", + "ethereum-common": "0.2.0", + "ethereumjs-tx": "^1.2.2", + "ethereumjs-util": "^5.0.0", + "merkle-patricia-tree": "^2.1.2" + }, + "dependencies": { + "ethereum-common": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/ethereum-common/-/ethereum-common-0.2.0.tgz", + "integrity": "sha512-XOnAR/3rntJgbCdGhqdaLIxDLWKLmsZOGhHdBKadEr6gEnJLH52k93Ou+TUdFaPN3hJc3isBZBal3U/XZ15abA==" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-common": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/ethereumjs-common/-/ethereumjs-common-1.5.2.tgz", + "integrity": "sha512-hTfZjwGX52GS2jcVO6E2sx4YuFnf0Fhp5ylo4pEPhEffNln7vS59Hr5sLnp3/QCazFLluuBZ+FZ6J5HTp0EqCA==" + }, + "ethereumjs-tx": { + "version": "1.3.7", + "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-1.3.7.tgz", + "integrity": "sha512-wvLMxzt1RPhAQ9Yi3/HKZTn0FZYpnsmQdbKYfUUpi4j1SEIcbkd9tndVjcPrufY3V7j2IebOpC00Zp2P/Ay2kA==", + "requires": { + "ethereum-common": "^0.0.18", + "ethereumjs-util": "^5.0.0" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-util": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-6.2.1.tgz", + "integrity": "sha512-W2Ktez4L01Vexijrm5EB6w7dg4n/TgpoYU4avuT5T3Vmnw/eCRtiBrJfQYS/DCSvDIOLn2k57GcHdeBcgVxAqw==", + "requires": { + "@types/bn.js": "^4.11.3", + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "0.1.6", + "rlp": "^2.2.3" + } + }, + "ethereumjs-vm": { + "version": "2.6.0", + "resolved": "https://registry.npmjs.org/ethereumjs-vm/-/ethereumjs-vm-2.6.0.tgz", + "integrity": "sha512-r/XIUik/ynGbxS3y+mvGnbOKnuLo40V5Mj1J25+HEO63aWYREIqvWeRO/hnROlMBE5WoniQmPmhiaN0ctiHaXw==", + "requires": { + "async": "^2.1.2", + "async-eventemitter": "^0.2.2", + "ethereumjs-account": "^2.0.3", + "ethereumjs-block": "~2.2.0", + "ethereumjs-common": "^1.1.0", + "ethereumjs-util": "^6.0.0", + "fake-merkle-patricia-tree": "^1.0.1", + "functional-red-black-tree": "^1.0.1", + "merkle-patricia-tree": "^2.3.2", + "rustbn.js": "~0.2.0", + "safe-buffer": "^5.1.1" + }, + "dependencies": { + "ethereumjs-block": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/ethereumjs-block/-/ethereumjs-block-2.2.2.tgz", + "integrity": "sha512-2p49ifhek3h2zeg/+da6XpdFR3GlqY3BIEiqxGF8j9aSRIgkb7M1Ky+yULBKJOu8PAZxfhsYA+HxUk2aCQp3vg==", + "requires": { + "async": "^2.0.1", + "ethereumjs-common": "^1.5.0", + "ethereumjs-tx": "^2.1.1", + "ethereumjs-util": "^5.0.0", + "merkle-patricia-tree": "^2.1.2" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + } + } + }, + "ethereumjs-tx": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ethereumjs-tx/-/ethereumjs-tx-2.1.2.tgz", + "integrity": "sha512-zZEK1onCeiORb0wyCXUvg94Ve5It/K6GD1K+26KfFKodiBiS6d9lfCXlUKGBBdQ+bv7Day+JK0tj1K+BeNFRAw==", + "requires": { + "ethereumjs-common": "^1.5.0", + "ethereumjs-util": "^6.0.0" + } + } + } + }, + "ethereumjs-wallet": { + "version": "0.6.5", + "resolved": "https://registry.npmjs.org/ethereumjs-wallet/-/ethereumjs-wallet-0.6.5.tgz", + "integrity": "sha512-MDwjwB9VQVnpp/Dc1XzA6J1a3wgHQ4hSvA1uWNatdpOrtCbPVuQSKSyRnjLvS0a+KKMw2pvQ9Ybqpb3+eW8oNA==", + "requires": { + "aes-js": "^3.1.1", + "bs58check": "^2.1.2", + "ethereum-cryptography": "^0.1.3", + "ethereumjs-util": "^6.0.0", + "randombytes": "^2.0.6", + "safe-buffer": "^5.1.2", + "scryptsy": "^1.2.1", + "utf8": "^3.0.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "aes-js": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.1.2.tgz", + "integrity": "sha512-e5pEa2kBnBOgR4Y/p20pskXI74UEz7de8ZGVo58asOtvSVG5YAbJeELPZxOmt+Bnz3rX753YKhfIn4X4l1PPRQ==" + }, + "scryptsy": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-1.2.1.tgz", + "integrity": "sha1-oyJfpLJST4AnAHYeKFW987LZIWM=", + "requires": { + "pbkdf2": "^3.0.3" + } + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "ethers": { + "version": "4.0.48", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.48.tgz", + "integrity": "sha512-sZD5K8H28dOrcidzx9f8KYh8083n5BexIO3+SbE4jK83L85FxtpXZBCQdXb8gkg+7sBqomcLhhkU7UHL+F7I2g==", + "requires": { + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.5.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.4", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + }, + "dependencies": { + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "scrypt-js": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.4.tgz", + "integrity": "sha512-4KsaGcPnuhtCZQCxFxN3GVYIhKFPTdLd8PLC552XwbMndtD0cjRFAhDuuydXQ0h08ZfPgzqe6EKHozpuH74iDw==" + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + } + } + }, + "ethjs-unit": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-unit/-/ethjs-unit-0.1.6.tgz", + "integrity": "sha1-xmWSHkduh7ziqdWIpv4EBbLEFpk=", + "requires": { + "bn.js": "4.11.6", + "number-to-bn": "1.7.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "ethjs-util": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/ethjs-util/-/ethjs-util-0.1.6.tgz", + "integrity": "sha512-CUnVOQq7gSpDHZVVrQW8ExxUETWrnrvXYvYz55wOU8Uj4VCgw56XC2B/fVqQN+f7gmrnRHSLVnFAwsCuNwji8w==", + "requires": { + "is-hex-prefixed": "1.0.0", + "strip-hex-prefix": "1.0.0" + } + }, + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==" + }, + "events": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/events/-/events-3.2.0.tgz", + "integrity": "sha512-/46HWwbfCX2xTawVfkKLGxMifJYQBWMwY1mjywRtb4c9x8l5NP3KoJtnIOiL1hfdRkIuYhETxQlo62IF8tcnlg==" + }, + "evp_bytestokey": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/evp_bytestokey/-/evp_bytestokey-1.0.3.tgz", + "integrity": "sha512-/f2Go4TognH/KvCISP7OUsHn85hT9nUkxxA9BEWxFn+Oj9o8ZNLm/40hdlgSLyuOimsrTKLUMEorQexp/aPQeA==", + "requires": { + "md5.js": "^1.3.4", + "safe-buffer": "^5.1.1" + } + }, + "express": { + "version": "4.17.1", + "resolved": "https://registry.npmjs.org/express/-/express-4.17.1.tgz", + "integrity": "sha512-mHJ9O79RqluphRrcw2X/GTh3k9tVv8YcoyY4Kkh4WDMUYKRZUq0h1o0w2rrrxBqM7VoeUVqgb27xlEMXTnYt4g==", + "requires": { + "accepts": "~1.3.7", + "array-flatten": "1.1.1", + "body-parser": "1.19.0", + "content-disposition": "0.5.3", + "content-type": "~1.0.4", + "cookie": "0.4.0", + "cookie-signature": "1.0.6", + "debug": "2.6.9", + "depd": "~1.1.2", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "finalhandler": "~1.1.2", + "fresh": "0.5.2", + "merge-descriptors": "1.0.1", + "methods": "~1.1.2", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "path-to-regexp": "0.1.7", + "proxy-addr": "~2.0.5", + "qs": "6.7.0", + "range-parser": "~1.2.1", + "safe-buffer": "5.1.2", + "send": "0.17.1", + "serve-static": "1.14.1", + "setprototypeof": "1.1.1", + "statuses": "~1.5.0", + "type-is": "~1.6.18", + "utils-merge": "1.0.1", + "vary": "~1.1.2" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "ext": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz", + "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==", + "requires": { + "type": "^2.0.0" + }, + "dependencies": { + "type": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/type/-/type-2.1.0.tgz", + "integrity": "sha512-G9absDWvhAWCV2gmF1zKud3OyC61nZDwWvBL2DApaVFogI07CprggiQAOOjvp2NRjYWFzPyu7vwtDrQFq8jeSA==" + } + } + }, + "extend": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", + "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==" + }, + "extsprintf": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz", + "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=" + }, + "fake-merkle-patricia-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/fake-merkle-patricia-tree/-/fake-merkle-patricia-tree-1.0.1.tgz", + "integrity": "sha1-S4w6z7Ugr635hgsfFM2M40As3dM=", + "requires": { + "checkpoint-store": "^1.1.0" + } + }, + "fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==" + }, + "fast-json-stable-stringify": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", + "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==" + }, + "fast-safe-stringify": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/fast-safe-stringify/-/fast-safe-stringify-2.0.7.tgz", + "integrity": "sha512-Utm6CdzT+6xsDk2m8S6uL8VHxNwI6Jub+e9NYTcAms28T84pTa25GJQV9j0CY0N1rM8hK4x6grpF2BQf+2qwVA==" + }, + "fd-slicer": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", + "integrity": "sha1-JcfInLH5B3+IkbvmHY85Dq4lbx4=", + "requires": { + "pend": "~1.2.0" + } + }, + "fetch-ponyfill": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/fetch-ponyfill/-/fetch-ponyfill-4.1.0.tgz", + "integrity": "sha1-rjzl9zLGReq4fkroeTQUcJsjmJM=", + "requires": { + "node-fetch": "~1.7.1" + }, + "dependencies": { + "node-fetch": { + "version": "1.7.3", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-1.7.3.tgz", + "integrity": "sha512-NhZ4CsKx7cYm2vSrBAr2PvFOe6sWDf0UYLRqA6svUYg7+/TSfVAu49jYC4BvQ4Sms9SZgdqGBgroqfDhJdTyKQ==", + "requires": { + "encoding": "^0.1.11", + "is-stream": "^1.0.1" + } + } + } + }, + "file-type": { + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/file-type/-/file-type-5.2.0.tgz", + "integrity": "sha1-LdvqfHP/42No365J3DOMBYwritY=" + }, + "file-uri-to-path": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/file-uri-to-path/-/file-uri-to-path-1.0.0.tgz", + "integrity": "sha512-0Zt+s3L7Vf1biwWZ29aARiVYLx7iMGnEUl9x33fbB/j3jR81u/O2LbqK+Bm1CDSNDKVtJ/YjwY7TUd5SkeLQLw==" + }, + "finalhandler": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/finalhandler/-/finalhandler-1.1.2.tgz", + "integrity": "sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==", + "requires": { + "debug": "2.6.9", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "on-finished": "~2.3.0", + "parseurl": "~1.3.3", + "statuses": "~1.5.0", + "unpipe": "~1.0.0" + } + }, + "for-each": { + "version": "0.3.3", + "resolved": "https://registry.npmjs.org/for-each/-/for-each-0.3.3.tgz", + "integrity": "sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==", + "requires": { + "is-callable": "^1.1.3" + } + }, + "forever-agent": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz", + "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=" + }, + "form-data": { + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz", + "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==", + "requires": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.6", + "mime-types": "^2.1.12" + } + }, + "forwarded": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/forwarded/-/forwarded-0.1.2.tgz", + "integrity": "sha1-mMI9qxF1ZXuMBXPozszZGw/xjIQ=" + }, + "fresh": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/fresh/-/fresh-0.5.2.tgz", + "integrity": "sha1-PYyt2Q2XZWn6g1qx+OSyOhBWBac=" + }, + "fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==" + }, + "fs-extra": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-4.0.3.tgz", + "integrity": "sha512-q6rbdDd1o2mAnQreO7YADIxf/Whx4AHBiRf6d+/cVT8h44ss+lHgxf1FemcqDnQt9X3ct4McHr+JMGlYSsK7Cg==", + "requires": { + "graceful-fs": "^4.1.2", + "jsonfile": "^4.0.0", + "universalify": "^0.1.0" + } + }, + "fs-minipass": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/fs-minipass/-/fs-minipass-1.2.7.tgz", + "integrity": "sha512-GWSSJGFy4e9GUeCcbIkED+bgAoFyj7XF1mV8rma3QW4NIqX9Kyx79N/PF61H5udOV3aY1IaMLs6pGbH71nlCTA==", + "requires": { + "minipass": "^2.6.0" + } + }, + "fs.realpath": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", + "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=" + }, + "function-bind": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz", + "integrity": "sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==" + }, + "functional-red-black-tree": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", + "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=" + }, + "get-stream": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-4.1.0.tgz", + "integrity": "sha512-GMat4EJ5161kIy2HevLlr4luNjBgvmj413KaQA7jt4V8B4RDsfpHk7WQ9GVqfYyyx8OS/L66Kox+rJRNklLK7w==", + "requires": { + "pump": "^3.0.0" + } + }, + "getpass": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz", + "integrity": "sha1-Xv+OPmhNVprkyysSgmBOi6YhSfo=", + "requires": { + "assert-plus": "^1.0.0" + } + }, + "glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "requires": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + } + }, + "global": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/global/-/global-4.3.2.tgz", + "integrity": "sha1-52mJJopsdMOJCLEwWxD8DjlOnQ8=", + "requires": { + "min-document": "^2.19.0", + "process": "~0.5.1" + } + }, + "globals": { + "version": "9.18.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-9.18.0.tgz", + "integrity": "sha512-S0nG3CLEQiY/ILxqtztTWH/3iRRdyBLw6KMDxnKMchrtbj2OFmehVh0WUCfW3DUrIgx/qFrJPICrq4Z4sTR9UQ==" + }, + "google-protobuf": { + "version": "3.13.0", + "resolved": "https://registry.npmjs.org/google-protobuf/-/google-protobuf-3.13.0.tgz", + "integrity": "sha512-ZIf3qfLFayVrPvAjeKKxO5FRF1/NwRxt6Dko+fWEMuHwHbZx8/fcaAao9b0wCM6kr8qeg2te8XTpyuvKuD9aKw==" + }, + "got": { + "version": "9.6.0", + "resolved": "https://registry.npmjs.org/got/-/got-9.6.0.tgz", + "integrity": "sha512-R7eWptXuGYxwijs0eV+v3o6+XH1IqVK8dJOEecQfTmkncw9AV4dcw/Dhxi8MdlqPthxxpZyizMzyg8RTmEsG+Q==", + "requires": { + "@sindresorhus/is": "^0.14.0", + "@szmarczak/http-timer": "^1.1.2", + "cacheable-request": "^6.0.0", + "decompress-response": "^3.3.0", + "duplexer3": "^0.1.4", + "get-stream": "^4.1.0", + "lowercase-keys": "^1.0.1", + "mimic-response": "^1.0.1", + "p-cancelable": "^1.0.0", + "to-readable-stream": "^1.0.0", + "url-parse-lax": "^3.0.0" + } + }, + "graceful-fs": { + "version": "4.2.4", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz", + "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==" + }, + "har-schema": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/har-schema/-/har-schema-2.0.0.tgz", + "integrity": "sha1-qUwiJOvKwEeCoNkDVSHyRzW37JI=" + }, + "har-validator": { + "version": "5.1.5", + "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz", + "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==", + "requires": { + "ajv": "^6.12.3", + "har-schema": "^2.0.0" + } + }, + "has": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/has/-/has-1.0.3.tgz", + "integrity": "sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==", + "requires": { + "function-bind": "^1.1.1" + } + }, + "has-ansi": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/has-ansi/-/has-ansi-2.0.0.tgz", + "integrity": "sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "has-symbol-support-x": { + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/has-symbol-support-x/-/has-symbol-support-x-1.4.2.tgz", + "integrity": "sha512-3ToOva++HaW+eCpgqZrCfN51IPB+7bJNVT6CUATzueB5Heb8o6Nam0V3HG5dlDvZU1Gn5QLcbahiKw/XVk5JJw==" + }, + "has-symbols": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.1.tgz", + "integrity": "sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==" + }, + "has-to-string-tag-x": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/has-to-string-tag-x/-/has-to-string-tag-x-1.4.1.tgz", + "integrity": "sha512-vdbKfmw+3LoOYVr+mtxHaX5a96+0f3DljYd8JOqvOLsf5mw2Otda2qCDT9qRqLAhrjyQ0h7ual5nOiASpsGNFw==", + "requires": { + "has-symbol-support-x": "^1.4.1" + } + }, + "hash-base": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/hash-base/-/hash-base-3.1.0.tgz", + "integrity": "sha512-1nmYp/rhMDiE7AYkDw+lLwlAzz0AntGIe51F3RfFfEqyQ3feY2eI/NcwC6umIQVOASPMsWJLJScWKSSvzL9IVA==", + "requires": { + "inherits": "^2.0.4", + "readable-stream": "^3.6.0", + "safe-buffer": "^5.2.0" + } + }, + "hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, + "hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha1-0nRXAQJabHdabFRXk+1QL8DGSaE=", + "requires": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "home-or-tmp": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/home-or-tmp/-/home-or-tmp-2.0.0.tgz", + "integrity": "sha1-42w/LSyufXRqhX440Y1fMqeILbg=", + "requires": { + "os-homedir": "^1.0.0", + "os-tmpdir": "^1.0.1" + } + }, + "http-cache-semantics": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/http-cache-semantics/-/http-cache-semantics-4.1.0.tgz", + "integrity": "sha512-carPklcUh7ROWRK7Cv27RPtdhYhUsela/ue5/jKzjegVvXDqM2ILE9Q2BGn9JZJh1g87cp56su/FgQSzcWS8cQ==" + }, + "http-errors": { + "version": "1.7.2", + "resolved": "https://registry.npmjs.org/http-errors/-/http-errors-1.7.2.tgz", + "integrity": "sha512-uUQBt3H/cSIVfch6i1EuPNy/YsRSOUBXTVfZ+yR7Zjez3qjBz6i9+i4zjNaoqcoFVI4lQJ5plg63TvGfRSDCRg==", + "requires": { + "depd": "~1.1.2", + "inherits": "2.0.3", + "setprototypeof": "1.1.1", + "statuses": ">= 1.5.0 < 2", + "toidentifier": "1.0.0" + }, + "dependencies": { + "inherits": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.3.tgz", + "integrity": "sha1-Yzwsg+PaQqUC9SRmAiSA9CCCYd4=" + } + } + }, + "http-https": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/http-https/-/http-https-1.0.0.tgz", + "integrity": "sha1-L5CN1fHbQGjAWM1ubUzjkskTOJs=" + }, + "http-signature": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz", + "integrity": "sha1-muzZJRFHcvPZW2WmCruPfBj7rOE=", + "requires": { + "assert-plus": "^1.0.0", + "jsprim": "^1.2.2", + "sshpk": "^1.7.0" + } + }, + "iconv-lite": { + "version": "0.4.24", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", + "integrity": "sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==", + "requires": { + "safer-buffer": ">= 2.1.2 < 3" + } + }, + "idna-uts46-hx": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/idna-uts46-hx/-/idna-uts46-hx-2.3.1.tgz", + "integrity": "sha512-PWoF9Keq6laYdIRwwCdhTPl60xRqAloYNMQLiyUnG42VjT53oW07BXIRM+NK7eQjzXjAk2gUvX9caRxlnF9TAA==", + "requires": { + "punycode": "2.1.0" + }, + "dependencies": { + "punycode": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.0.tgz", + "integrity": "sha1-X4Y+3Im5bbCQdLrXlHvwkFbKTn0=" + } + } + }, + "ieee754": { + "version": "1.1.13", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.1.13.tgz", + "integrity": "sha512-4vf7I2LYV/HaWerSo3XmlMkp5eZ83i+/CDluXi/IGTs/O1sejBNhTtnxzmRZfvOUqj7lZjqHkeTvpgSFDlWZTg==" + }, + "immediate": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.3.0.tgz", + "integrity": "sha512-HR7EVodfFUdQCTIeySw+WDRFJlPcLOJbXfwwZ7Oom6tjsvZ3bOkCDJHehQC3nxJrv7+f9XecwazynjU8e4Vw3Q==" + }, + "inflight": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", + "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "requires": { + "once": "^1.3.0", + "wrappy": "1" + } + }, + "inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==" + }, + "invariant": { + "version": "2.2.4", + "resolved": "https://registry.npmjs.org/invariant/-/invariant-2.2.4.tgz", + "integrity": "sha512-phJfQVBuaJM5raOpJjSfkiD6BpbCE4Ns//LaXl6wGYtUBY83nWS6Rf9tXm2e8VaK60JEjYldbPif/A2B1C2gNA==", + "requires": { + "loose-envify": "^1.0.0" + } + }, + "ipaddr.js": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/ipaddr.js/-/ipaddr.js-1.9.1.tgz", + "integrity": "sha512-0KI/607xoxSToH7GjN1FfSbLoU0+btTicjsQSWQlh/hZykN8KpmMf7uYwPW3R+akZ6R/w18ZlXSHBYXiYUPO3g==" + }, + "is-arguments": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/is-arguments/-/is-arguments-1.0.4.tgz", + "integrity": "sha512-xPh0Rmt8NE65sNzvyUmWgI1tz3mKq74lGA0mL8LYZcoIzKOzDh6HmrYm3d18k60nHerC8A9Km8kYu87zfSFnLA==" + }, + "is-callable": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-callable/-/is-callable-1.2.0.tgz", + "integrity": "sha512-pyVD9AaGLxtg6srb2Ng6ynWJqkHU9bEM087AKck0w8QwDarTfNcpIYoU8x8Hv2Icm8u6kFJM18Dag8lyqGkviw==" + }, + "is-date-object": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-date-object/-/is-date-object-1.0.2.tgz", + "integrity": "sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==" + }, + "is-finite": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-finite/-/is-finite-1.1.0.tgz", + "integrity": "sha512-cdyMtqX/BOqqNBBiKlIVkytNHm49MtMlYyn1zxzvJKWmFMlGzm+ry5BBfYyeY9YmNKbRSo/o7OX9w9ale0wg3w==" + }, + "is-fn": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-fn/-/is-fn-1.0.0.tgz", + "integrity": "sha1-lUPV3nvPWwiiLsiiC65uKG1RDYw=" + }, + "is-function": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/is-function/-/is-function-1.0.2.tgz", + "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ==" + }, + "is-hex-prefixed": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-hex-prefixed/-/is-hex-prefixed-1.0.0.tgz", + "integrity": "sha1-fY035q135dEnFIkTxXPggtd39VQ=" + }, + "is-natural-number": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/is-natural-number/-/is-natural-number-4.0.1.tgz", + "integrity": "sha1-q5124dtM7VHjXeDHLr7PCfc0zeg=" + }, + "is-object": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/is-object/-/is-object-1.0.1.tgz", + "integrity": "sha1-iVJojF7C/9awPsyF52ngKQMINHA=" + }, + "is-plain-obj": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-1.1.0.tgz", + "integrity": "sha1-caUMhCnfync8kqOQpKA7OfzVHT4=" + }, + "is-regex": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.0.5.tgz", + "integrity": "sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==", + "requires": { + "has": "^1.0.3" + } + }, + "is-retry-allowed": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/is-retry-allowed/-/is-retry-allowed-1.2.0.tgz", + "integrity": "sha512-RUbUeKwvm3XG2VYamhJL1xFktgjvPzL0Hq8C+6yrWIswDy3BIXGqCxhxkc30N9jqK311gVU137K8Ei55/zVJRg==" + }, + "is-stream": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-1.1.0.tgz", + "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=" + }, + "is-symbol": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/is-symbol/-/is-symbol-1.0.3.tgz", + "integrity": "sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==", + "requires": { + "has-symbols": "^1.0.1" + } + }, + "is-typedarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", + "integrity": "sha1-5HnICFjfDBsR3dppQPlgEfzaSpo=" + }, + "isarray": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", + "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=" + }, + "isstream": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/isstream/-/isstream-0.1.2.tgz", + "integrity": "sha1-R+Y/evVa+m+S4VAOaQ64uFKcCZo=" + }, + "isurl": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/isurl/-/isurl-1.0.0.tgz", + "integrity": "sha512-1P/yWsxPlDtn7QeRD+ULKQPaIaN6yF368GZ2vDfv0AL0NwpStafjWCDDdn0k8wgFMWpVAqG7oJhxHnlud42i9w==", + "requires": { + "has-to-string-tag-x": "^1.2.0", + "is-object": "^1.0.1" + } + }, + "js-sha3": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.5.7.tgz", + "integrity": "sha1-DU/9gALVMzqrr0oj7tL2N0yfKOc=" + }, + "js-tokens": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-3.0.2.tgz", + "integrity": "sha1-mGbfOVECEw449/mWvOtlRDIJwls=" + }, + "jsbn": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/jsbn/-/jsbn-0.1.1.tgz", + "integrity": "sha1-peZUwuWi3rXyAdls77yoDA7y9RM=" + }, + "jsesc": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz", + "integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=" + }, + "json-buffer": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.0.tgz", + "integrity": "sha1-Wx85evx11ne96Lz8Dkfh+aPZqJg=" + }, + "json-rpc-engine": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/json-rpc-engine/-/json-rpc-engine-5.3.0.tgz", + "integrity": "sha512-+diJ9s8rxB+fbJhT7ZEf8r8spaLRignLd8jTgQ/h5JSGppAHGtNMZtCoabipCaleR1B3GTGxbXBOqhaJSGmPGQ==", + "requires": { + "eth-rpc-errors": "^3.0.0", + "safe-event-emitter": "^1.0.1" + } + }, + "json-rpc-error": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/json-rpc-error/-/json-rpc-error-2.0.0.tgz", + "integrity": "sha1-p6+cICg4tekFxyUOVH8a/3cligI=", + "requires": { + "inherits": "^2.0.1" + } + }, + "json-rpc-random-id": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-rpc-random-id/-/json-rpc-random-id-1.0.1.tgz", + "integrity": "sha1-uknZat7RRE27jaPSA3SKy7zeyMg=" + }, + "json-schema": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/json-schema/-/json-schema-0.2.3.tgz", + "integrity": "sha1-tIDIkuWaLwWVTOcnvT8qTogvnhM=" + }, + "json-schema-traverse": { + "version": "0.4.1", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", + "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==" + }, + "json-stable-stringify": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz", + "integrity": "sha1-mnWdOcXy/1A/1TAGRu1EX4jE+a8=", + "requires": { + "jsonify": "~0.0.0" + } + }, + "json-stringify-safe": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz", + "integrity": "sha1-Epai1Y/UXxmg9s4B1lcB4sc1tus=" + }, + "json5": { + "version": "0.5.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-0.5.1.tgz", + "integrity": "sha1-Hq3nrMASA0rYTiOWdn6tn6VJWCE=" + }, + "jsonfile": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", + "integrity": "sha1-h3Gq4HmbZAdrdmQPygWPnBDjPss=", + "requires": { + "graceful-fs": "^4.1.6" + } + }, + "jsonify": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/jsonify/-/jsonify-0.0.0.tgz", + "integrity": "sha1-LHS27kHZPKUbe1qu6PUDYx0lKnM=" + }, + "jsprim": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", + "integrity": "sha1-MT5mvB5cwG5Di8G3SZwuXFastqI=", + "requires": { + "assert-plus": "1.0.0", + "extsprintf": "1.3.0", + "json-schema": "0.2.3", + "verror": "1.10.0" + } + }, + "keccak": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/keccak/-/keccak-3.0.1.tgz", + "integrity": "sha512-epq90L9jlFWCW7+pQa6JOnKn2Xgl2mtI664seYR6MHskvI9agt7AnDqmAlp9TqU4/caMYbA08Hi5DMZAl5zdkA==", + "requires": { + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "keyv": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-3.1.0.tgz", + "integrity": "sha512-9ykJ/46SN/9KPM/sichzQ7OvXyGDYKGTaDlKMGCAlg2UK8KRy4jb0d8sFc+0Tt0YYnThq8X2RZgCg74RPxgcVA==", + "requires": { + "json-buffer": "3.0.0" + } + }, + "level-codec": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/level-codec/-/level-codec-7.0.1.tgz", + "integrity": "sha512-Ua/R9B9r3RasXdRmOtd+t9TCOEIIlts+TN/7XTT2unhDaL6sJn83S3rUyljbr6lVtw49N3/yA0HHjpV6Kzb2aQ==" + }, + "level-errors": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/level-errors/-/level-errors-1.0.5.tgz", + "integrity": "sha512-/cLUpQduF6bNrWuAC4pwtUKA5t669pCsCi2XbmojG2tFeOr9j6ShtdDCtFFQO1DRt+EVZhx9gPzP9G2bUaG4ig==", + "requires": { + "errno": "~0.1.1" + } + }, + "level-iterator-stream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/level-iterator-stream/-/level-iterator-stream-1.3.1.tgz", + "integrity": "sha1-5Dt4sagUPm+pek9IXrjqUwNS8u0=", + "requires": { + "inherits": "^2.0.1", + "level-errors": "^1.0.3", + "readable-stream": "^1.0.33", + "xtend": "^4.0.0" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.1.14", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.1.14.tgz", + "integrity": "sha1-fPTFTvZI44EwhMY23SB54WbAgdk=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + } + } + }, + "level-ws": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/level-ws/-/level-ws-0.0.0.tgz", + "integrity": "sha1-Ny5RIXeSSgBCSwtDrvK7QkltIos=", + "requires": { + "readable-stream": "~1.0.15", + "xtend": "~2.1.1" + }, + "dependencies": { + "isarray": { + "version": "0.0.1", + "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", + "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" + }, + "readable-stream": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.34.tgz", + "integrity": "sha1-Elgg40vIQtLyqq+v5MKRbuMsFXw=", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.1", + "isarray": "0.0.1", + "string_decoder": "~0.10.x" + } + }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=" + }, + "xtend": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-2.1.2.tgz", + "integrity": "sha1-bv7MKk2tjmlixJAbM3znuoe10os=", + "requires": { + "object-keys": "~0.4.0" + } + } + } + }, + "levelup": { + "version": "1.3.9", + "resolved": "https://registry.npmjs.org/levelup/-/levelup-1.3.9.tgz", + "integrity": "sha512-VVGHfKIlmw8w1XqpGOAGwq6sZm2WwWLmlDcULkKWQXEA5EopA8OBNJ2Ck2v6bdk8HeEZSbCSEgzXadyQFm76sQ==", + "requires": { + "deferred-leveldown": "~1.2.1", + "level-codec": "~7.0.0", + "level-errors": "~1.0.3", + "level-iterator-stream": "~1.3.0", + "prr": "~1.0.1", + "semver": "~5.4.1", + "xtend": "~4.0.0" + }, + "dependencies": { + "semver": { + "version": "5.4.1", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.4.1.tgz", + "integrity": "sha512-WfG/X9+oATh81XtllIo/I8gOiY9EXRdv1cQdyykeXK17YcUW3EXUAi2To4pcH6nZtJPr7ZOpM5OMyWJZm+8Rsg==" + } + } + }, + "lodash": { + "version": "4.17.20", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.20.tgz", + "integrity": "sha512-PlhdFcillOINfeV7Ni6oF1TAEayyZBoZ8bcshTHqOYJYlrqzRK5hagpagky5o4HfCzzd1TRkXPMFq6cKk9rGmA==" + }, + "lodash.flatmap": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/lodash.flatmap/-/lodash.flatmap-4.5.0.tgz", + "integrity": "sha1-74y/QI9uSCaGYzRTBcaswLd4cC4=" + }, + "long": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/long/-/long-4.0.0.tgz", + "integrity": "sha512-XsP+KhQif4bjX1kbuSiySJFNAehNxgLb6hPRGJ9QsUr8ajHkuXGdrHmFUTUUXhDwVX2R5bY4JNZEwbUiMhV+MA==" + }, + "loose-envify": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz", + "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==", + "requires": { + "js-tokens": "^3.0.0 || ^4.0.0" + } + }, + "lowercase-keys": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/lowercase-keys/-/lowercase-keys-1.0.1.tgz", + "integrity": "sha512-G2Lj61tXDnVFFOi8VZds+SoQjtQC3dgokKdDG2mTm1tx4m50NUHBOZSBwQQHyy0V12A0JTG4icfZQH+xPyh8VA==" + }, + "ltgt": { + "version": "2.2.1", + "resolved": "https://registry.npmjs.org/ltgt/-/ltgt-2.2.1.tgz", + "integrity": "sha1-81ypHEk/e3PaDgdJUwTxezH4fuU=" + }, + "make-dir": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-1.3.0.tgz", + "integrity": "sha512-2w31R7SJtieJJnQtGc7RVL2StM2vGYVfqUOvUDxH6bC6aJTxPxTF0GnIgCyu7tjockiUWAYQRbxa7vKn34s5sQ==", + "requires": { + "pify": "^3.0.0" + }, + "dependencies": { + "pify": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-3.0.0.tgz", + "integrity": "sha1-5aSs0sEB/fPZpNB/DbxNtJ3SgXY=" + } + } + }, + "md5.js": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/md5.js/-/md5.js-1.3.5.tgz", + "integrity": "sha512-xitP+WxNPcTTOgnTJcrhM0xvdPepipPSf3I8EIpGKeFLjt3PlJLIDG3u8EX53ZIubkb+5U2+3rELYpEhHhzdkg==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "media-typer": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/media-typer/-/media-typer-0.3.0.tgz", + "integrity": "sha1-hxDXrwqmJvj/+hzgAWhUUmMlV0g=" + }, + "memdown": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/memdown/-/memdown-1.4.1.tgz", + "integrity": "sha1-tOThkhdGZP+65BNhqlAPMRnv4hU=", + "requires": { + "abstract-leveldown": "~2.7.1", + "functional-red-black-tree": "^1.0.1", + "immediate": "^3.2.3", + "inherits": "~2.0.1", + "ltgt": "~2.2.0", + "safe-buffer": "~5.1.1" + }, + "dependencies": { + "abstract-leveldown": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/abstract-leveldown/-/abstract-leveldown-2.7.2.tgz", + "integrity": "sha512-+OVvxH2rHVEhWLdbudP6p0+dNMXu8JA1CbhP19T8paTYAcX7oJ4OVjT+ZUVpv7mITxXHqDMej+GdqXBmXkw09w==", + "requires": { + "xtend": "~4.0.0" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "merge-descriptors": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/merge-descriptors/-/merge-descriptors-1.0.1.tgz", + "integrity": "sha1-sAqqVW3YtEVoFQ7J0blT8/kMu2E=" + }, + "merkle-patricia-tree": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/merkle-patricia-tree/-/merkle-patricia-tree-2.3.2.tgz", + "integrity": "sha512-81PW5m8oz/pz3GvsAwbauj7Y00rqm81Tzad77tHBwU7pIAtN+TJnMSOJhxBKflSVYhptMMb9RskhqHqrSm1V+g==", + "requires": { + "async": "^1.4.2", + "ethereumjs-util": "^5.0.0", + "level-ws": "0.0.0", + "levelup": "^1.2.1", + "memdown": "^1.0.0", + "readable-stream": "^2.0.0", + "rlp": "^2.0.0", + "semaphore": ">=1.0.1" + }, + "dependencies": { + "async": { + "version": "1.5.2", + "resolved": "https://registry.npmjs.org/async/-/async-1.5.2.tgz", + "integrity": "sha1-7GphrlZIDAw8skHJVhjiCJL5Zyo=" + }, + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + } + } + }, + "methods": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz", + "integrity": "sha1-VSmk1nZUE07cxSZmVoNbD4Ua/O4=" + }, + "miller-rabin": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/miller-rabin/-/miller-rabin-4.0.1.tgz", + "integrity": "sha512-115fLhvZVqWwHPbClyntxEVfVDfl9DLLTuJvq3g2O/Oxi8AiNouAHvDSzHS0viUJc+V5vm3eq91Xwqn9dp4jRA==", + "requires": { + "bn.js": "^4.0.0", + "brorand": "^1.0.1" + } + }, + "mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==" + }, + "mime-db": { + "version": "1.44.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.44.0.tgz", + "integrity": "sha512-/NOTfLrsPBVeH7YtFPgsVWveuL+4SjjYxaQ1xtM1KMFj7HdxlBlxeyNLzhyJVx7r4rZGJAZ/6lkKCitSc/Nmpg==" + }, + "mime-types": { + "version": "2.1.27", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.27.tgz", + "integrity": "sha512-JIhqnCasI9yD+SsmkquHBxTSEuZdQX5BuQnS2Vc7puQQQ+8yiP5AY5uWhpdv4YL4VM5c6iliiYWPgJ/nJQLp7w==", + "requires": { + "mime-db": "1.44.0" + } + }, + "mimic-response": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-1.0.1.tgz", + "integrity": "sha512-j5EctnkH7amfV/q5Hgmoal1g2QHFJRraOtmx0JpIqkxhBhI/lJSl1nMpQ45hVarwNETOoWEimndZ4QK0RHxuxQ==" + }, + "min-document": { + "version": "2.19.0", + "resolved": "https://registry.npmjs.org/min-document/-/min-document-2.19.0.tgz", + "integrity": "sha1-e9KC4/WELtKVu3SM3Z8f+iyCRoU=", + "requires": { + "dom-walk": "^0.1.0" + } + }, + "minimalistic-assert": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", + "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" + }, + "minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha1-9sAMHAsIIkblxNmd+4x8CDsrWCo=" + }, + "minimatch": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", + "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "requires": { + "brace-expansion": "^1.1.7" + } + }, + "minimist": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", + "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==" + }, + "minipass": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-2.9.0.tgz", + "integrity": "sha512-wxfUjg9WebH+CUDX/CdbRlh5SmfZiy/hpkxaRI16Y9W56Pa75sWgd/rvFilSgrauD9NyFymP/+JFV3KwzIsJeg==", + "requires": { + "safe-buffer": "^5.1.2", + "yallist": "^3.0.0" + } + }, + "minizlib": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/minizlib/-/minizlib-1.3.3.tgz", + "integrity": "sha512-6ZYMOEnmVsdCeTJVE0W9ZD+pVnE8h9Hma/iOwwRDsdQoePpoX56/8B6z3P9VNwppJuBKNRuFDRNRqRWexT9G9Q==", + "requires": { + "minipass": "^2.9.0" + } + }, + "mkdirp": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", + "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==" + }, + "mkdirp-promise": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/mkdirp-promise/-/mkdirp-promise-5.0.1.tgz", + "integrity": "sha1-6bj2jlUsaKnBcTuEiD96HdA5uKE=", + "requires": { + "mkdirp": "*" + } + }, + "mock-fs": { + "version": "4.13.0", + "resolved": "https://registry.npmjs.org/mock-fs/-/mock-fs-4.13.0.tgz", + "integrity": "sha512-DD0vOdofJdoaRNtnWcrXe6RQbpHkPPmtqGq14uRX0F8ZKJ5nv89CVTYl/BZdppDxBDaV0hl75htg3abpEWlPZA==" + }, + "ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=" + }, + "nacl": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/nacl/-/nacl-0.1.3.tgz", + "integrity": "sha1-kfJ8OMumJGGEIzNOBR6PdvPWN0s=" + }, + "nan": { + "version": "2.14.1", + "resolved": "https://registry.npmjs.org/nan/-/nan-2.14.1.tgz", + "integrity": "sha512-isWHgVjnFjh2x2yuJ/tj3JbwoHu3UC2dX5G/88Cm24yB6YopVgxvBObDY7n5xW6ExmFhJpSEQqFPvq9zaXc8Jw==" + }, + "nano-json-stream-parser": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/nano-json-stream-parser/-/nano-json-stream-parser-0.1.2.tgz", + "integrity": "sha1-DMj20OK2IrR5xA1JnEbWS3Vcb18=" + }, + "nanoassert": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/nanoassert/-/nanoassert-1.1.0.tgz", + "integrity": "sha1-TzFS4JVA/eKMdvRLGbvNHVpCR40=" + }, + "negotiator": { + "version": "0.6.2", + "resolved": "https://registry.npmjs.org/negotiator/-/negotiator-0.6.2.tgz", + "integrity": "sha512-hZXc7K2e+PgeI1eDBe/10Ard4ekbfrrqG8Ep+8Jmf4JID2bNg7NvCPOZN+kfF574pFQI7mum2AUqDidoKqcTOw==" + }, + "next-tick": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.0.0.tgz", + "integrity": "sha1-yobR/ogoFpsBICCOPchCS524NCw=" + }, + "node-addon-api": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", + "integrity": "sha512-Ntyt4AIXyaLIuMHF6IOoTakB3K+RWxwtsHNRxllEoA6vPwP9o4866g6YWDLUdnucilZhmkxiHwHr11gAENw+QA==" + }, + "node-fetch": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.1.2.tgz", + "integrity": "sha1-q4hOjn5X44qUR1POxwb3iNF2i7U=" + }, + "node-gyp-build": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/node-gyp-build/-/node-gyp-build-4.2.3.tgz", + "integrity": "sha512-MN6ZpzmfNCRM+3t57PTJHgHyw/h4OWnZ6mR8P5j/uZtqQr46RRuDE/P+g3n0YR/AiYXeWixZZzaip77gdICfRg==" + }, + "normalize-url": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/normalize-url/-/normalize-url-4.5.0.tgz", + "integrity": "sha512-2s47yzUxdexf1OhyRi4Em83iQk0aPvwTddtFz4hnSSw9dCEsLEGf6SwIO8ss/19S9iBb5sJaOuTvTGDeZI00BQ==" + }, + "number-to-bn": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/number-to-bn/-/number-to-bn-1.7.0.tgz", + "integrity": "sha1-uzYjWS9+X54AMLGXe9QaDFP+HqA=", + "requires": { + "bn.js": "4.11.6", + "strip-hex-prefix": "1.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.6", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.6.tgz", + "integrity": "sha1-UzRK2xRhehP26N0s4okF0cC6MhU=" + } + } + }, + "oauth-sign": { + "version": "0.9.0", + "resolved": "https://registry.npmjs.org/oauth-sign/-/oauth-sign-0.9.0.tgz", + "integrity": "sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==" + }, + "object-assign": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/object-assign/-/object-assign-4.1.1.tgz", + "integrity": "sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=" + }, + "object-inspect": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.7.0.tgz", + "integrity": "sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==" + }, + "object-is": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/object-is/-/object-is-1.1.2.tgz", + "integrity": "sha512-5lHCz+0uufF6wZ7CRFWJN3hp8Jqblpgve06U5CMQ3f//6iDjPr2PEo9MWCjEssDsa+UZEL4PkFpr+BMop6aKzQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "object-keys": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-0.4.0.tgz", + "integrity": "sha1-KKaq50KN0sOpLz2V8hM13SBOAzY=" + }, + "object.assign": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/object.assign/-/object.assign-4.1.0.tgz", + "integrity": "sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==", + "requires": { + "define-properties": "^1.1.2", + "function-bind": "^1.1.1", + "has-symbols": "^1.0.0", + "object-keys": "^1.0.11" + }, + "dependencies": { + "object-keys": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/object-keys/-/object-keys-1.1.1.tgz", + "integrity": "sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==" + } + } + }, + "oboe": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/oboe/-/oboe-2.1.4.tgz", + "integrity": "sha1-IMiM2wwVNxuwQRklfU/dNLCqSfY=", + "requires": { + "http-https": "^1.0.0" + } + }, + "on-finished": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.3.0.tgz", + "integrity": "sha1-IPEzZIGwg811M3mSoWlxqi2QaUc=", + "requires": { + "ee-first": "1.1.1" + } + }, + "once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", + "requires": { + "wrappy": "1" + } + }, + "os-homedir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-homedir/-/os-homedir-1.0.2.tgz", + "integrity": "sha1-/7xJiDNuDoM94MFox+8VISGqf7M=" + }, + "os-tmpdir": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/os-tmpdir/-/os-tmpdir-1.0.2.tgz", + "integrity": "sha1-u+Z0BseaqFxc/sdm/lc0VV36EnQ=" + }, + "p-cancelable": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-1.1.0.tgz", + "integrity": "sha512-s73XxOZ4zpt1edZYZzvhqFa6uvQc1vwUa0K0BdtIZgQMAJj9IbebH+JkgKZc9h+B05PKHLOTl4ajG1BmNrVZlw==" + }, + "p-finally": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/p-finally/-/p-finally-1.0.0.tgz", + "integrity": "sha1-P7z7FbiZpEEjs0ttzBi3JDNqLK4=" + }, + "p-timeout": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-1.2.1.tgz", + "integrity": "sha1-XrOzU7f86Z8QGhA4iAuwVOu+o4Y=", + "requires": { + "p-finally": "^1.0.0" + } + }, + "parse-asn1": { + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/parse-asn1/-/parse-asn1-5.1.6.tgz", + "integrity": "sha512-RnZRo1EPU6JBnra2vGHj0yhp6ebyjBZpmUCLHWiFhxlzvBCCpAuZ7elsBp1PVAbQN0/04VD/19rfzlBSwLstMw==", + "requires": { + "asn1.js": "^5.2.0", + "browserify-aes": "^1.0.0", + "evp_bytestokey": "^1.0.0", + "pbkdf2": "^3.0.3", + "safe-buffer": "^5.1.1" + } + }, + "parse-headers": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/parse-headers/-/parse-headers-2.0.3.tgz", + "integrity": "sha512-QhhZ+DCCit2Coi2vmAKbq5RGTRcQUOE2+REgv8vdyu7MnYx2eZztegqtTx99TZ86GTIwqiy3+4nQTWZ2tgmdCA==" + }, + "parseurl": { + "version": "1.3.3", + "resolved": "https://registry.npmjs.org/parseurl/-/parseurl-1.3.3.tgz", + "integrity": "sha512-CiyeOxFT/JZyN5m0z9PfXw4SCBJ6Sygz1Dpl0wqjlhDEGGBP1GnsUVEL0p63hoG1fcj3fHynXi9NYO4nWOL+qQ==" + }, + "path-is-absolute": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", + "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=" + }, + "path-parse": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.6.tgz", + "integrity": "sha512-GSmOT2EbHrINBf9SR7CDELwlJ8AENk3Qn7OikK4nFYAu3Ote2+JYNVvkpAEQm3/TLNEJFD/xZJjzyxg3KBWOzw==" + }, + "path-to-regexp": { + "version": "0.1.7", + "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-0.1.7.tgz", + "integrity": "sha1-32BBeABfUi8V60SQ5yR6G/qmf4w=" + }, + "pbkdf2": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/pbkdf2/-/pbkdf2-3.1.1.tgz", + "integrity": "sha512-4Ejy1OPxi9f2tt1rRV7Go7zmfDQ+ZectEQz3VGUQhgq62HtIRPDyG/JtnwIxs6x3uNMwo2V7q1fMvKjb+Tnpqg==", + "requires": { + "create-hash": "^1.1.2", + "create-hmac": "^1.1.4", + "ripemd160": "^2.0.1", + "safe-buffer": "^5.0.1", + "sha.js": "^2.4.8" + } + }, + "pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha1-elfrVQpng/kRUzH89GY9XI4AelA=" + }, + "performance-now": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/performance-now/-/performance-now-2.1.0.tgz", + "integrity": "sha1-Ywn04OX6kT7BxpMHrjZLSzd8nns=" + }, + "pify": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/pify/-/pify-2.3.0.tgz", + "integrity": "sha1-7RQaasBDqEnqWISY59yosVMw6Qw=" + }, + "pinkie": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/pinkie/-/pinkie-2.0.4.tgz", + "integrity": "sha1-clVrgM+g1IqXToDnckjoDtT3+HA=" + }, + "pinkie-promise": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/pinkie-promise/-/pinkie-promise-2.0.1.tgz", + "integrity": "sha1-ITXW36ejWMBprJsXh3YogihFD/o=", + "requires": { + "pinkie": "^2.0.0" + } + }, + "precond": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/precond/-/precond-0.2.3.tgz", + "integrity": "sha1-qpWRvKokkj8eD0hJ0kD0fvwQdaw=" + }, + "prepend-http": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-2.0.0.tgz", + "integrity": "sha1-6SQ0v6XqjBn0HN/UAddBo8gZ2Jc=" + }, + "private": { + "version": "0.1.8", + "resolved": "https://registry.npmjs.org/private/-/private-0.1.8.tgz", + "integrity": "sha512-VvivMrbvd2nKkiG38qjULzlc+4Vx4wm/whI9pQD35YrARNnhxeiRktSOhSukRLFNlzg6Br/cJPet5J/u19r/mg==" + }, + "process": { + "version": "0.5.2", + "resolved": "https://registry.npmjs.org/process/-/process-0.5.2.tgz", + "integrity": "sha1-FjjYqONML0QKkduVq5rrZ3/Bhc8=" + }, + "process-nextick-args": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", + "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==" + }, + "promise-to-callback": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/promise-to-callback/-/promise-to-callback-1.0.0.tgz", + "integrity": "sha1-XSp0kBC/tn2WNZj805YHRqaP7vc=", + "requires": { + "is-fn": "^1.0.0", + "set-immediate-shim": "^1.0.1" + } + }, + "protobufjs": { + "version": "6.10.1", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-6.10.1.tgz", + "integrity": "sha512-pb8kTchL+1Ceg4lFd5XUpK8PdWacbvV5SK2ULH2ebrYtl4GjJmS24m6CKME67jzV53tbJxHlnNOSqQHbTsR9JQ==", + "requires": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/long": "^4.0.1", + "@types/node": "^13.7.0", + "long": "^4.0.0" + }, + "dependencies": { + "@types/node": { + "version": "13.13.16", + "resolved": "https://registry.npmjs.org/@types/node/-/node-13.13.16.tgz", + "integrity": "sha512-dJ9vXxJ8MEwzNn4GkoAGauejhXoKuJyYKegsA6Af25ZpEDXomeVXt5HUWUNVHk5UN7+U0f6ghC6otwt+7PdSDg==" + } + } + }, + "proxy-addr": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.6.tgz", + "integrity": "sha512-dh/frvCBVmSsDYzw6n926jv974gddhkFPfiN8hPOi30Wax25QZyZEGveluCgliBnqmuM+UJmBErbAUFIoDbjOw==", + "requires": { + "forwarded": "~0.1.2", + "ipaddr.js": "1.9.1" + } + }, + "prr": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/prr/-/prr-1.0.1.tgz", + "integrity": "sha1-0/wRS6BplaRexok/SEzrHXj19HY=" + }, + "psl": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/psl/-/psl-1.8.0.tgz", + "integrity": "sha512-RIdOzyoavK+hA18OGGWDqUTsCLhtA7IcZ/6NCs4fFJaHBDab+pDDmDIByWFRQJq2Cd7r1OoQxBGKOaztq+hjIQ==" + }, + "public-encrypt": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/public-encrypt/-/public-encrypt-4.0.3.tgz", + "integrity": "sha512-zVpa8oKZSz5bTMTFClc1fQOnyyEzpl5ozpi1B5YcvBrdohMjH2rfsBtyXcuNuwjsDIXmBYlF2N5FlJYhR29t8Q==", + "requires": { + "bn.js": "^4.1.0", + "browserify-rsa": "^4.0.0", + "create-hash": "^1.1.0", + "parse-asn1": "^5.0.0", + "randombytes": "^2.0.1", + "safe-buffer": "^5.1.2" + } + }, + "pump": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.0.tgz", + "integrity": "sha512-LwZy+p3SFs1Pytd/jYct4wpv49HiYCqd9Rlc5ZVdk0V+8Yzv6jR5Blk3TRmPL1ft69TxP0IMZGJ+WPFU2BFhww==", + "requires": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "punycode": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", + "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==" + }, + "qs": { + "version": "6.7.0", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.7.0.tgz", + "integrity": "sha512-VCdBRNFTX1fyE7Nb6FYoURo/SPe62QCaAyzJvUjwRaIsc+NePBEniHlvxFmmX56+HZphIGtV0XeCirBtpDrTyQ==" + }, + "query-string": { + "version": "5.1.1", + "resolved": "https://registry.npmjs.org/query-string/-/query-string-5.1.1.tgz", + "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==", + "requires": { + "decode-uri-component": "^0.2.0", + "object-assign": "^4.1.0", + "strict-uri-encode": "^1.0.0" + } + }, + "randombytes": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", + "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", + "requires": { + "safe-buffer": "^5.1.0" + } + }, + "randomfill": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/randomfill/-/randomfill-1.0.4.tgz", + "integrity": "sha512-87lcbR8+MhcWcUiQ+9e+Rwx8MyR2P7qnt15ynUlbm3TU/fjbgz4GsvfSUDTemtCCtVCqb4ZcEFlyPNTh9bBTLw==", + "requires": { + "randombytes": "^2.0.5", + "safe-buffer": "^5.1.0" + } + }, + "randomhex": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/randomhex/-/randomhex-0.1.5.tgz", + "integrity": "sha1-us7vmCMpCRQA8qKRLGzQLxCU9YU=" + }, + "range-parser": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/range-parser/-/range-parser-1.2.1.tgz", + "integrity": "sha512-Hrgsx+orqoygnmhFbKaHE6c296J+HTAQXoxEF6gNupROmmGJRoyzfG3ccAveqCBrwr/2yxQ5BVd/GTl5agOwSg==" + }, + "raw-body": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/raw-body/-/raw-body-2.4.0.tgz", + "integrity": "sha512-4Oz8DUIwdvoa5qMJelxipzi/iJIi40O5cGV1wNYp5hvZP8ZN0T+jiNkL0QepXs+EsQ9XJ8ipEDoiH70ySUJP3Q==", + "requires": { + "bytes": "3.1.0", + "http-errors": "1.7.2", + "iconv-lite": "0.4.24", + "unpipe": "1.0.0" + } + }, + "readable-stream": { + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", + "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "requires": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + } + }, + "regenerate": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/regenerate/-/regenerate-1.4.1.tgz", + "integrity": "sha512-j2+C8+NtXQgEKWk49MMP5P/u2GhnahTtVkRIHr5R5lVRlbKvmQ+oS+A5aLKWp2ma5VkT8sh6v+v4hbH0YHR66A==" + }, + "regenerator-runtime": { + "version": "0.13.7", + "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz", + "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==" + }, + "regenerator-transform": { + "version": "0.10.1", + "resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.10.1.tgz", + "integrity": "sha512-PJepbvDbuK1xgIgnau7Y90cwaAmO/LCLMI2mPvaXq2heGMR3aWW5/BQvYrhJ8jgmQjXewXvBjzfqKcVOmhjZ6Q==", + "requires": { + "babel-runtime": "^6.18.0", + "babel-types": "^6.19.0", + "private": "^0.1.6" + } + }, + "regexp.prototype.flags": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/regexp.prototype.flags/-/regexp.prototype.flags-1.3.0.tgz", + "integrity": "sha512-2+Q0C5g951OlYlJz6yu5/M33IcsESLlLfsyIaLJaG4FA2r4yP8MvVMJUUP/fVBkSpbbbZlS5gynbEWLipiiXiQ==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1" + } + }, + "regexpu-core": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-2.0.0.tgz", + "integrity": "sha1-SdA4g3uNz4v6W5pCE5k45uoq4kA=", + "requires": { + "regenerate": "^1.2.1", + "regjsgen": "^0.2.0", + "regjsparser": "^0.1.4" + } + }, + "regjsgen": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz", + "integrity": "sha1-bAFq3qxVT3WCP+N6wFuS1aTtsfc=" + }, + "regjsparser": { + "version": "0.1.5", + "resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.1.5.tgz", + "integrity": "sha1-fuj4Tcb6eS0/0K4ijSS9lJ6tIFw=", + "requires": { + "jsesc": "~0.5.0" + } + }, + "repeating": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/repeating/-/repeating-2.0.1.tgz", + "integrity": "sha1-UhTFOpJtNVJwdSf7q0FdvAjQbdo=", + "requires": { + "is-finite": "^1.0.0" + } + }, + "request": { + "version": "2.88.2", + "resolved": "https://registry.npmjs.org/request/-/request-2.88.2.tgz", + "integrity": "sha512-MsvtOrfG9ZcrOwAW+Qi+F6HbD0CWXEh9ou77uOb7FM2WPhwT7smM833PzanhJLsgXjN89Ir6V2PczXNnMpwKhw==", + "requires": { + "aws-sign2": "~0.7.0", + "aws4": "^1.8.0", + "caseless": "~0.12.0", + "combined-stream": "~1.0.6", + "extend": "~3.0.2", + "forever-agent": "~0.6.1", + "form-data": "~2.3.2", + "har-validator": "~5.1.3", + "http-signature": "~1.2.0", + "is-typedarray": "~1.0.0", + "isstream": "~0.1.2", + "json-stringify-safe": "~5.0.1", + "mime-types": "~2.1.19", + "oauth-sign": "~0.9.0", + "performance-now": "^2.1.0", + "qs": "~6.5.2", + "safe-buffer": "^5.1.2", + "tough-cookie": "~2.5.0", + "tunnel-agent": "^0.6.0", + "uuid": "^3.3.2" + }, + "dependencies": { + "qs": { + "version": "6.5.2", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.5.2.tgz", + "integrity": "sha512-N5ZAX4/LxJmF+7wN74pUD6qAh9/wnvdQcjq9TZjevvXzSUo7bfmw91saqMjzGS2xq91/odN2dW/WOl7qQHNDGA==" + }, + "uuid": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.4.0.tgz", + "integrity": "sha512-HjSDRw6gZE5JMggctHBcjVak08+KEVhSIiDzFnT9S9aegmp85S/bReBVTb4QTFaRNptJ9kuYaNhnbNEOkbKb/A==" + } + } + }, + "resolve": { + "version": "1.17.0", + "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz", + "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==", + "requires": { + "path-parse": "^1.0.6" + } + }, + "responselike": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/responselike/-/responselike-1.0.2.tgz", + "integrity": "sha1-kYcg7ztjHFZCvgaPFa3lpG9Loec=", + "requires": { + "lowercase-keys": "^1.0.0" + } + }, + "resumer": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/resumer/-/resumer-0.0.0.tgz", + "integrity": "sha1-8ej0YeQGS6Oegq883CqMiT0HZ1k=", + "requires": { + "through": "~2.3.4" + } + }, + "ripemd160": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/ripemd160/-/ripemd160-2.0.2.tgz", + "integrity": "sha512-ii4iagi25WusVoiC4B4lq7pbXfAp3D9v5CwfkY33vffw2+pkDjY1D8GaN7spsxvCSx8dkPqOZCEZyfxcmJG2IA==", + "requires": { + "hash-base": "^3.0.0", + "inherits": "^2.0.1" + } + }, + "rlp": { + "version": "2.2.6", + "resolved": "https://registry.npmjs.org/rlp/-/rlp-2.2.6.tgz", + "integrity": "sha512-HAfAmL6SDYNWPUOJNrM500x4Thn4PZsEy5pijPh40U9WfNk0z15hUYzO9xVIMAdIHdFtD8CBDHd75Td1g36Mjg==", + "requires": { + "bn.js": "^4.11.1" + } + }, + "rustbn.js": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/rustbn.js/-/rustbn.js-0.2.0.tgz", + "integrity": "sha512-4VlvkRUuCJvr2J6Y0ImW7NvTCriMi7ErOAqWk1y69vAdoNIzCF3yPmgeNzx+RQTLEDFq5sHfscn1MwHxP9hNfA==" + }, + "safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==" + }, + "safe-event-emitter": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/safe-event-emitter/-/safe-event-emitter-1.0.1.tgz", + "integrity": "sha512-e1wFe99A91XYYxoQbcq2ZJUWurxEyP8vfz7A7vuUe1s95q8r5ebraVaA1BukYJcpM6V16ugWoD9vngi8Ccu5fg==", + "requires": { + "events": "^3.0.0" + } + }, + "safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==" + }, + "scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, + "scryptsy": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/scryptsy/-/scryptsy-2.1.0.tgz", + "integrity": "sha512-1CdSqHQowJBnMAFyPEBRfqag/YP9OF394FV+4YREIJX4ljD7OxvQRDayyoyyCk+senRjSkP6VnUNQmVQqB6g7w==" + }, + "secp256k1": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/secp256k1/-/secp256k1-4.0.2.tgz", + "integrity": "sha512-UDar4sKvWAksIlfX3xIaQReADn+WFnHvbVujpcbr+9Sf/69odMwy2MUsz5CKLQgX9nsIyrjuxL2imVyoNHa3fg==", + "requires": { + "elliptic": "^6.5.2", + "node-addon-api": "^2.0.0", + "node-gyp-build": "^4.2.0" + } + }, + "seek-bzip": { + "version": "1.0.6", + "resolved": "https://registry.npmjs.org/seek-bzip/-/seek-bzip-1.0.6.tgz", + "integrity": "sha512-e1QtP3YL5tWww8uKaOCQ18UxIT2laNBXHjV/S2WYCiK4udiv8lkG89KRIoCjUagnAmCBurjF4zEVX2ByBbnCjQ==", + "requires": { + "commander": "^2.8.1" + } + }, + "semaphore": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/semaphore/-/semaphore-1.1.0.tgz", + "integrity": "sha512-O4OZEaNtkMd/K0i6js9SL+gqy0ZCBMgUvlSqHKi4IBdjhe7wB8pwztUk1BbZ1fmrvpwFrPbHzqd2w5pTcJH6LA==" + }, + "semver": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/semver/-/semver-6.2.0.tgz", + "integrity": "sha512-jdFC1VdUGT/2Scgbimf7FSx9iJLXoqfglSF+gJeuNWVpiE37OIbc1jywR/GJyFdz3mnkz2/id0L0J/cr0izR5A==" + }, + "send": { + "version": "0.17.1", + "resolved": "https://registry.npmjs.org/send/-/send-0.17.1.tgz", + "integrity": "sha512-BsVKsiGcQMFwT8UxypobUKyv7irCNRHk1T0G680vk88yf6LBByGcZJOTJCrTP2xVN6yI+XjPJcNuE3V4fT9sAg==", + "requires": { + "debug": "2.6.9", + "depd": "~1.1.2", + "destroy": "~1.0.4", + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "etag": "~1.8.1", + "fresh": "0.5.2", + "http-errors": "~1.7.2", + "mime": "1.6.0", + "ms": "2.1.1", + "on-finished": "~2.3.0", + "range-parser": "~1.2.1", + "statuses": "~1.5.0" + }, + "dependencies": { + "ms": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.1.tgz", + "integrity": "sha512-tgp+dl5cGk28utYktBsrFqA7HKgrhgPsg6Z/EfhWI4gl1Hwq8B/GmY/0oXZ6nF8hDVesS/FpnYaD/kOWhYQvyg==" + } + } + }, + "serve-static": { + "version": "1.14.1", + "resolved": "https://registry.npmjs.org/serve-static/-/serve-static-1.14.1.tgz", + "integrity": "sha512-JMrvUwE54emCYWlTI+hGrGv5I8dEwmco/00EvkzIIsR7MqrHonbD9pO2MOfFnpFntl7ecpZs+3mW+XbQZu9QCg==", + "requires": { + "encodeurl": "~1.0.2", + "escape-html": "~1.0.3", + "parseurl": "~1.3.3", + "send": "0.17.1" + } + }, + "servify": { + "version": "0.1.12", + "resolved": "https://registry.npmjs.org/servify/-/servify-0.1.12.tgz", + "integrity": "sha512-/xE6GvsKKqyo1BAY+KxOWXcLpPsUUyji7Qg3bVD7hh1eRze5bR1uYiuDA/k3Gof1s9BTzQZEJK8sNcNGFIzeWw==", + "requires": { + "body-parser": "^1.16.0", + "cors": "^2.8.1", + "express": "^4.14.0", + "request": "^2.79.0", + "xhr": "^2.3.3" + } + }, + "set-immediate-shim": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/set-immediate-shim/-/set-immediate-shim-1.0.1.tgz", + "integrity": "sha1-SysbJ+uAip+NzEgaWOXlb1mfP2E=" + }, + "setimmediate": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", + "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=" + }, + "setprototypeof": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/setprototypeof/-/setprototypeof-1.1.1.tgz", + "integrity": "sha512-JvdAWfbXeIGaZ9cILp38HntZSFSo3mWg6xGcJJsd+d4aRMOqauag1C63dJfDw7OaMYwEbHMOxEZ1lqVRYP2OAw==" + }, + "sha.js": { + "version": "2.4.11", + "resolved": "https://registry.npmjs.org/sha.js/-/sha.js-2.4.11.tgz", + "integrity": "sha512-QMEp5B7cftE7APOjk5Y6xgrbWu+WkLVQwk8JNjZ8nKRciZaByEW6MubieAiToS7+dwvrjGhH8jRXz3MVd0AYqQ==", + "requires": { + "inherits": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==" + }, + "simple-get": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-2.8.1.tgz", + "integrity": "sha512-lSSHRSw3mQNUGPAYRqo7xy9dhKmxFXIjLjp4KHpf99GEH2VH7C3AM+Qfx6du6jhfUi6Vm7XnbEVEf7Wb6N8jRw==", + "requires": { + "decompress-response": "^3.3.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "slash": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-1.0.0.tgz", + "integrity": "sha1-xB8vbDn8FtHNF61LXYlhFK5HDVU=" + }, + "source-map": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", + "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==" + }, + "source-map-support": { + "version": "0.5.19", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", + "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "requires": { + "buffer-from": "^1.0.0", + "source-map": "^0.6.0" + } + }, + "sshpk": { + "version": "1.16.1", + "resolved": "https://registry.npmjs.org/sshpk/-/sshpk-1.16.1.tgz", + "integrity": "sha512-HXXqVUq7+pcKeLqqZj6mHFUMvXtOJt1uoUx09pFW6011inTMxqI8BA8PM95myrIyyKwdnzjdFjLiE6KBPVtJIg==", + "requires": { + "asn1": "~0.2.3", + "assert-plus": "^1.0.0", + "bcrypt-pbkdf": "^1.0.0", + "dashdash": "^1.12.0", + "ecc-jsbn": "~0.1.1", + "getpass": "^0.1.1", + "jsbn": "~0.1.0", + "safer-buffer": "^2.0.2", + "tweetnacl": "~0.14.0" + }, + "dependencies": { + "tweetnacl": { + "version": "0.14.5", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-0.14.5.tgz", + "integrity": "sha1-WuaBd/GS1EViadEIr6k/+HQ/T2Q=" + } + } + }, + "statuses": { + "version": "1.5.0", + "resolved": "https://registry.npmjs.org/statuses/-/statuses-1.5.0.tgz", + "integrity": "sha1-Fhx9rBd2Wf2YEfQ3cfqZOBR4Yow=" + }, + "strict-uri-encode": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz", + "integrity": "sha1-J5siXfHVgrH1TmWt3UNS4Y+qBxM=" + }, + "string.prototype.trim": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/string.prototype.trim/-/string.prototype.trim-1.2.1.tgz", + "integrity": "sha512-MjGFEeqixw47dAMFMtgUro/I0+wNqZB5GKXGt1fFr24u3TzDXCPu7J9Buppzoe3r/LqkSDLDDJzE15RGWDGAVw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.0-next.1", + "function-bind": "^1.1.1" + } + }, + "string.prototype.trimend": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimend/-/string.prototype.trimend-1.0.1.tgz", + "integrity": "sha512-LRPxFUaTtpqYsTeNKaFOw3R4bxIzWOnbQ837QfBylo8jIxtcbK/A/sMV7Q+OAV/vWo+7s25pOE10KYSjaSO06g==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string.prototype.trimstart": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/string.prototype.trimstart/-/string.prototype.trimstart-1.0.1.tgz", + "integrity": "sha512-XxZn+QpvrBI1FOcg6dIpxUPgWCPuNXvMD72aaRaUQv1eD4e/Qy8i/hFTe0BUmD60p/QA6bh1avmuPTfNjqVWRw==", + "requires": { + "define-properties": "^1.1.3", + "es-abstract": "^1.17.5" + } + }, + "string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "requires": { + "safe-buffer": "~5.2.0" + } + }, + "strip-ansi": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-3.0.1.tgz", + "integrity": "sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=", + "requires": { + "ansi-regex": "^2.0.0" + } + }, + "strip-dirs": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/strip-dirs/-/strip-dirs-2.1.0.tgz", + "integrity": "sha512-JOCxOeKLm2CAS73y/U4ZeZPTkE+gNVCzKt7Eox84Iej1LT/2pTWYpZKJuxwQpvX1LiZb1xokNR7RLfuBAa7T3g==", + "requires": { + "is-natural-number": "^4.0.1" + } + }, + "strip-hex-prefix": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/strip-hex-prefix/-/strip-hex-prefix-1.0.0.tgz", + "integrity": "sha1-DF8VX+8RUTczd96du1iNoFUA428=", + "requires": { + "is-hex-prefixed": "1.0.0" + } + }, + "supports-color": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-2.0.0.tgz", + "integrity": "sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=" + }, + "swarm-js": { + "version": "0.1.39", + "resolved": "https://registry.npmjs.org/swarm-js/-/swarm-js-0.1.39.tgz", + "integrity": "sha512-QLMqL2rzF6n5s50BptyD6Oi0R1aWlJC5Y17SRIVXRj6OR1DRIPM7nepvrxxkjA1zNzFz6mUOMjfeqeDaWB7OOg==", + "requires": { + "bluebird": "^3.5.0", + "buffer": "^5.0.5", + "decompress": "^4.0.0", + "eth-lib": "^0.1.26", + "fs-extra": "^4.0.2", + "got": "^7.1.0", + "mime-types": "^2.1.16", + "mkdirp-promise": "^5.0.1", + "mock-fs": "^4.1.0", + "setimmediate": "^1.0.5", + "tar": "^4.0.2", + "xhr-request-promise": "^0.1.2" + }, + "dependencies": { + "get-stream": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-3.0.0.tgz", + "integrity": "sha1-jpQ9E1jcN1VQVOy+LtsFqhdO3hQ=" + }, + "got": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/got/-/got-7.1.0.tgz", + "integrity": "sha512-Y5WMo7xKKq1muPsxD+KmrR8DH5auG7fBdDVueZwETwV6VytKyU9OX/ddpq2/1hp1vIPvVb4T81dKQz3BivkNLw==", + "requires": { + "decompress-response": "^3.2.0", + "duplexer3": "^0.1.4", + "get-stream": "^3.0.0", + "is-plain-obj": "^1.1.0", + "is-retry-allowed": "^1.0.0", + "is-stream": "^1.0.0", + "isurl": "^1.0.0-alpha5", + "lowercase-keys": "^1.0.0", + "p-cancelable": "^0.3.0", + "p-timeout": "^1.1.1", + "safe-buffer": "^5.0.1", + "timed-out": "^4.0.0", + "url-parse-lax": "^1.0.0", + "url-to-options": "^1.0.1" + } + }, + "p-cancelable": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/p-cancelable/-/p-cancelable-0.3.0.tgz", + "integrity": "sha512-RVbZPLso8+jFeq1MfNvgXtCRED2raz/dKpacfTNxsx6pLEpEomM7gah6VeHSYV3+vo0OAi4MkArtQcWWXuQoyw==" + }, + "prepend-http": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/prepend-http/-/prepend-http-1.0.4.tgz", + "integrity": "sha1-1PRWKwzjaW5BrFLQ4ALlemNdxtw=" + }, + "url-parse-lax": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-1.0.0.tgz", + "integrity": "sha1-evjzA2Rem9eaJy56FKxovAYJ2nM=", + "requires": { + "prepend-http": "^1.0.1" + } + } + } + }, + "tape": { + "version": "4.13.3", + "resolved": "https://registry.npmjs.org/tape/-/tape-4.13.3.tgz", + "integrity": "sha512-0/Y20PwRIUkQcTCSi4AASs+OANZZwqPKaipGCEwp10dQMipVvSZwUUCi01Y/OklIGyHKFhIcjock+DKnBfLAFw==", + "requires": { + "deep-equal": "~1.1.1", + "defined": "~1.0.0", + "dotignore": "~0.1.2", + "for-each": "~0.3.3", + "function-bind": "~1.1.1", + "glob": "~7.1.6", + "has": "~1.0.3", + "inherits": "~2.0.4", + "is-regex": "~1.0.5", + "minimist": "~1.2.5", + "object-inspect": "~1.7.0", + "resolve": "~1.17.0", + "resumer": "~0.0.0", + "string.prototype.trim": "~1.2.1", + "through": "~2.3.8" + } + }, + "tar": { + "version": "4.4.13", + "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.13.tgz", + "integrity": "sha512-w2VwSrBoHa5BsSyH+KxEqeQBAllHhccyMFVHtGtdMpF4W7IRWfZjFiQceJPChOeTsSDVUpER2T8FA93pr0L+QA==", + "requires": { + "chownr": "^1.1.1", + "fs-minipass": "^1.2.5", + "minipass": "^2.8.6", + "minizlib": "^1.2.1", + "mkdirp": "^0.5.0", + "safe-buffer": "^5.1.2", + "yallist": "^3.0.3" + }, + "dependencies": { + "mkdirp": { + "version": "0.5.5", + "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", + "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", + "requires": { + "minimist": "^1.2.5" + } + } + } + }, + "tar-stream": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-1.6.2.tgz", + "integrity": "sha512-rzS0heiNf8Xn7/mpdSVVSMAWAoy9bfb1WOTYC78Z0UQKeKa/CWS8FOq0lKGNa8DWKAn9gxjCvMLYc5PGXYlK2A==", + "requires": { + "bl": "^1.0.0", + "buffer-alloc": "^1.2.0", + "end-of-stream": "^1.0.0", + "fs-constants": "^1.0.0", + "readable-stream": "^2.3.0", + "to-buffer": "^1.1.1", + "xtend": "^4.0.0" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "through": { + "version": "2.3.8", + "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz", + "integrity": "sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=" + }, + "through2": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/through2/-/through2-2.0.5.tgz", + "integrity": "sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==", + "requires": { + "readable-stream": "~2.3.6", + "xtend": "~4.0.1" + }, + "dependencies": { + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + } + }, + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + } + } + } + }, + "timed-out": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/timed-out/-/timed-out-4.0.1.tgz", + "integrity": "sha1-8y6srFoXW+ol1/q1Zas+2HQe9W8=" + }, + "to-buffer": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/to-buffer/-/to-buffer-1.1.1.tgz", + "integrity": "sha512-lx9B5iv7msuFYE3dytT+KE5tap+rNYw+K4jVkb9R/asAb+pbBSM17jtunHplhBe6RRJdZx3Pn2Jph24O32mOVg==" + }, + "to-fast-properties": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", + "integrity": "sha1-3F5pjL0HkmW8c+A3doGk5Og/YW4=" + }, + "to-readable-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/to-readable-stream/-/to-readable-stream-1.0.0.tgz", + "integrity": "sha512-Iq25XBt6zD5npPhlLVXGFN3/gyR2/qODcKNNyTMd4vbm39HUaOiAM4PMq0eMVC/Tkxz+Zjdsc55g9yyz+Yq00Q==" + }, + "toidentifier": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/toidentifier/-/toidentifier-1.0.0.tgz", + "integrity": "sha512-yaOH/Pk/VEhBWWTlhI+qXxDFXlejDGcQipMlyxda9nthulaxLZUNcUqFxokp0vcYnvteJln5FNQDRrxj3YcbVw==" + }, + "tough-cookie": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz", + "integrity": "sha512-nlLsUzgm1kfLXSXfRZMc1KLAugd4hqJHDTvc2hDIwS3mZAfMEuMbc03SujMF+GEcpaX/qboeycw6iO8JwVv2+g==", + "requires": { + "psl": "^1.1.28", + "punycode": "^2.1.1" + } + }, + "trim-right": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/trim-right/-/trim-right-1.0.1.tgz", + "integrity": "sha1-yy4SAwZ+DI3h9hQJS5/kVwTqYAM=" + }, + "truffle-hdwallet-provider": { + "version": "1.0.17", + "resolved": "https://registry.npmjs.org/truffle-hdwallet-provider/-/truffle-hdwallet-provider-1.0.17.tgz", + "integrity": "sha512-s6DvSP83jiIAc6TUcpr7Uqnja1+sLGJ8og3X7n41vfyC4OCaKmBtXL5HOHf+SsU3iblOvnbFDgmN6Y1VBL/fsg==", + "requires": { + "any-promise": "^1.3.0", + "bindings": "^1.3.1", + "web3": "1.2.1", + "websocket": "^1.0.28" + } + }, + "ts-protoc-gen": { + "version": "0.12.0", + "resolved": "https://registry.npmjs.org/ts-protoc-gen/-/ts-protoc-gen-0.12.0.tgz", + "integrity": "sha512-V7jnICJxKqalBrnJSMTW5tB9sGi48gOC325bfcM7TDNUItVOlaMM//rQmuo49ybipk/SyJTnWXgtJnhHCevNJw==", + "requires": { + "google-protobuf": "^3.6.1" + } + }, + "tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha1-J6XeoGs2sEoKmWZ3SykIaPD8QP0=", + "requires": { + "safe-buffer": "^5.0.1" + } + }, + "tweetnacl": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/tweetnacl/-/tweetnacl-1.0.3.tgz", + "integrity": "sha512-6rt+RN7aOi1nGMyC4Xa5DdYiukl2UWCbcJft7YhxReBGQD7OAM8Pbxw6YMo4r2diNEA8FEmu32YOn9rhaiE5yw==" + }, + "type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, + "type-is": { + "version": "1.6.18", + "resolved": "https://registry.npmjs.org/type-is/-/type-is-1.6.18.tgz", + "integrity": "sha512-TkRKr9sUTxEH8MdfuCSP7VizJyzRNMjj2J2do2Jr3Kym598JVdEksuzPQCnlFPW4ky9Q+iA+ma9BGm06XQBy8g==", + "requires": { + "media-typer": "0.3.0", + "mime-types": "~2.1.24" + } + }, + "typedarray": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/typedarray/-/typedarray-0.0.6.tgz", + "integrity": "sha1-hnrHTjhkGHsdPUfZlqeOxciDB3c=" + }, + "typedarray-to-buffer": { + "version": "3.1.5", + "resolved": "https://registry.npmjs.org/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz", + "integrity": "sha512-zdu8XMNEDepKKR+XYOXAVPtWui0ly0NtohUscw+UmaHiAWT8hrV1rr//H6V+0DvJ3OQ19S979M0laLfX8rm82Q==", + "requires": { + "is-typedarray": "^1.0.0" + } + }, + "ultron": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", + "integrity": "sha512-UIEXBNeYmKptWH6z8ZnqTeS8fV74zG0/eRU9VGkpzz+LIJNs8W/zM/L+7ctCkRrgbNnnR0xxw4bKOr0cW0N0Og==" + }, + "unbzip2-stream": { + "version": "1.4.3", + "resolved": "https://registry.npmjs.org/unbzip2-stream/-/unbzip2-stream-1.4.3.tgz", + "integrity": "sha512-mlExGW4w71ebDJviH16lQLtZS32VKqsSfk80GCfUlwT/4/hNRFsoscrF/c++9xinkMzECL1uL9DDwXqFWkruPg==", + "requires": { + "buffer": "^5.2.1", + "through": "^2.3.8" + } + }, + "underscore": { + "version": "1.9.1", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.9.1.tgz", + "integrity": "sha512-5/4etnCkd9c8gwgowi5/om/mYO5ajCaOgdzj/oW+0eQV9WxKBDZw5+ycmKmeaTXjInS/W0BzpGLo2xR2aBwZdg==" + }, + "universalify": { + "version": "0.1.2", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", + "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==" + }, + "unorm": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/unorm/-/unorm-1.6.0.tgz", + "integrity": "sha512-b2/KCUlYZUeA7JFUuRJZPUtr4gZvBh7tavtv4fvk4+KV9pfGiR6CQAQAWl49ZpR3ts2dk4FYkP7EIgDJoiOLDA==" + }, + "unpipe": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", + "integrity": "sha1-sr9O6FFKrmFltIF4KdIbLvSZBOw=" + }, + "uri-js": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", + "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "requires": { + "punycode": "^2.1.0" + } + }, + "url-parse-lax": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/url-parse-lax/-/url-parse-lax-3.0.0.tgz", + "integrity": "sha1-FrXK/Afb42dsGxmZF3gj1lA6yww=", + "requires": { + "prepend-http": "^2.0.0" + } + }, + "url-set-query": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/url-set-query/-/url-set-query-1.0.0.tgz", + "integrity": "sha1-AW6M/Xwg7gXK/neV6JK9BwL6ozk=" + }, + "url-to-options": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/url-to-options/-/url-to-options-1.0.1.tgz", + "integrity": "sha1-FQWgOiiaSMvXpDTvuu7FBV9WM6k=" + }, + "utf8": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/utf8/-/utf8-3.0.0.tgz", + "integrity": "sha512-E8VjFIQ/TyQgp+TZfS6l8yp/xWppSAHzidGiRrqe4bK4XP9pTRyKFgGJpO3SN7zdX4DeomTrwaseCHovfpFcqQ==" + }, + "util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=" + }, + "utils-merge": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz", + "integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM=" + }, + "uuid": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-2.0.1.tgz", + "integrity": "sha1-wqMN7bPlNdcsz4LjQ5QaULqFM6w=" + }, + "vary": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz", + "integrity": "sha1-IpnwLG3tMNSllhsLn3RSShj2NPw=" + }, + "verror": { + "version": "1.10.0", + "resolved": "https://registry.npmjs.org/verror/-/verror-1.10.0.tgz", + "integrity": "sha1-OhBcoXBTr1XW4nDB+CiGguGNpAA=", + "requires": { + "assert-plus": "^1.0.0", + "core-util-is": "1.0.2", + "extsprintf": "^1.2.0" + } + }, + "web3": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3/-/web3-1.2.1.tgz", + "integrity": "sha512-nNMzeCK0agb5i/oTWNdQ1aGtwYfXzHottFP2Dz0oGIzavPMGSKyVlr8ibVb1yK5sJBjrWVnTdGaOC2zKDFuFRw==", + "requires": { + "web3-bzz": "1.2.1", + "web3-core": "1.2.1", + "web3-eth": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-shh": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-bzz": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-bzz/-/web3-bzz-1.2.1.tgz", + "integrity": "sha512-LdOO44TuYbGIPfL4ilkuS89GQovxUpmLz6C1UC7VYVVRILeZS740FVB3j9V4P4FHUk1RenaDfKhcntqgVCHtjw==", + "requires": { + "got": "9.6.0", + "swarm-js": "0.1.39", + "underscore": "1.9.1" + } + }, + "web3-core": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core/-/web3-core-1.2.1.tgz", + "integrity": "sha512-5ODwIqgl8oIg/0+Ai4jsLxkKFWJYE0uLuE1yUKHNVCL4zL6n3rFjRMpKPokd6id6nJCNgeA64KdWQ4XfpnjdMg==", + "requires": { + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-requestmanager": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-helpers": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-helpers/-/web3-core-helpers-1.2.1.tgz", + "integrity": "sha512-Gx3sTEajD5r96bJgfuW377PZVFmXIH4TdqDhgGwd2lZQCcMi+DA4TgxJNJGxn0R3aUVzyyE76j4LBrh412mXrw==", + "requires": { + "underscore": "1.9.1", + "web3-eth-iban": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-method": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-method/-/web3-core-method-1.2.1.tgz", + "integrity": "sha512-Ghg2WS23qi6Xj8Od3VCzaImLHseEA7/usvnOItluiIc5cKs00WYWsNy2YRStzU9a2+z8lwQywPYp0nTzR/QXdQ==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-core-promievent": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-promievent/-/web3-core-promievent-1.2.1.tgz", + "integrity": "sha512-IVUqgpIKoeOYblwpex4Hye6npM0aMR+kU49VP06secPeN0rHMyhGF0ZGveWBrGvf8WDPI7jhqPBFIC6Jf3Q3zw==", + "requires": { + "any-promise": "1.3.0", + "eventemitter3": "3.1.2" + } + }, + "web3-core-requestmanager": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-requestmanager/-/web3-core-requestmanager-1.2.1.tgz", + "integrity": "sha512-xfknTC69RfYmLKC+83Jz73IC3/sS2ZLhGtX33D4Q5nQ8yc39ElyAolxr9sJQS8kihOcM6u4J+8gyGMqsLcpIBg==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "web3-providers-http": "1.2.1", + "web3-providers-ipc": "1.2.1", + "web3-providers-ws": "1.2.1" + } + }, + "web3-core-subscriptions": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-core-subscriptions/-/web3-core-subscriptions-1.2.1.tgz", + "integrity": "sha512-nmOwe3NsB8V8UFsY1r+sW6KjdOS68h8nuh7NzlWxBQT/19QSUGiERRTaZXWu5BYvo1EoZRMxCKyCQpSSXLc08g==", + "requires": { + "eventemitter3": "3.1.2", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + } + }, + "web3-eth": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth/-/web3-eth-1.2.1.tgz", + "integrity": "sha512-/2xly4Yry5FW1i+uygPjhfvgUP/MS/Dk+PDqmzp5M88tS86A+j8BzKc23GrlA8sgGs0645cpZK/999LpEF5UdA==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-accounts": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-eth-ens": "1.2.1", + "web3-eth-iban": "1.2.1", + "web3-eth-personal": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-abi": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-abi/-/web3-eth-abi-1.2.1.tgz", + "integrity": "sha512-jI/KhU2a/DQPZXHjo2GW0myEljzfiKOn+h1qxK1+Y9OQfTcBMxrQJyH5AP89O6l6NZ1QvNdq99ThAxBFoy5L+g==", + "requires": { + "ethers": "4.0.0-beta.3", + "underscore": "1.9.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "@types/node": { + "version": "10.17.29", + "resolved": "https://registry.npmjs.org/@types/node/-/node-10.17.29.tgz", + "integrity": "sha512-zLo9rjUeQ5+QVhOufDwrb3XKyso31fJBJnk9wUUQIBDExF/O4LryvpOfozfUaxgqifTnlt7FyqsAPXUq5yFZSA==" + }, + "elliptic": { + "version": "6.3.3", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.3.3.tgz", + "integrity": "sha1-VILZZG1UvLif19mU/J4ulWiHbj8=", + "requires": { + "bn.js": "^4.4.0", + "brorand": "^1.0.1", + "hash.js": "^1.0.0", + "inherits": "^2.0.1" + } + }, + "ethers": { + "version": "4.0.0-beta.3", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-4.0.0-beta.3.tgz", + "integrity": "sha512-YYPogooSknTwvHg3+Mv71gM/3Wcrx+ZpCzarBj3mqs9njjRkrOo2/eufzhHloOCo3JSoNI4TQJJ6yU5ABm3Uog==", + "requires": { + "@types/node": "^10.3.2", + "aes-js": "3.0.0", + "bn.js": "^4.4.0", + "elliptic": "6.3.3", + "hash.js": "1.1.3", + "js-sha3": "0.5.7", + "scrypt-js": "2.0.3", + "setimmediate": "1.0.4", + "uuid": "2.0.1", + "xmlhttprequest": "1.8.0" + } + }, + "hash.js": { + "version": "1.1.3", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.3.tgz", + "integrity": "sha512-/UETyP0W22QILqS+6HowevwhEFJ3MBJnwTf75Qob9Wz9t0DPuisL8kW8YZMK62dHAKE1c1p+gY1TtOLY+USEHA==", + "requires": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.0" + } + }, + "scrypt-js": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-2.0.3.tgz", + "integrity": "sha1-uwBAvgMEPamgEqLOqfyfhSz8h9Q=" + }, + "setimmediate": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.4.tgz", + "integrity": "sha1-IOgd5iLUoCWIzgyNqJc8vPHTE48=" + } + } + }, + "web3-eth-accounts": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-accounts/-/web3-eth-accounts-1.2.1.tgz", + "integrity": "sha512-26I4qq42STQ8IeKUyur3MdQ1NzrzCqPsmzqpux0j6X/XBD7EjZ+Cs0lhGNkSKH5dI3V8CJasnQ5T1mNKeWB7nQ==", + "requires": { + "any-promise": "1.3.0", + "crypto-browserify": "3.12.0", + "eth-lib": "0.2.7", + "scryptsy": "2.1.0", + "semver": "6.2.0", + "underscore": "1.9.1", + "uuid": "3.3.2", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + }, + "dependencies": { + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + }, + "uuid": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-3.3.2.tgz", + "integrity": "sha512-yXJmeNaw3DnnKAOKJE51sL/ZaYfWJRl1pK9dr19YFCu0ObS231AB1/LbqTKRAQ5kw8A90rA6fr4riOUpTZvQZA==" + } + } + }, + "web3-eth-contract": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-contract/-/web3-eth-contract-1.2.1.tgz", + "integrity": "sha512-kYFESbQ3boC9bl2rYVghj7O8UKMiuKaiMkxvRH5cEDHil8V7MGEGZNH0slSdoyeftZVlaWSMqkRP/chfnKND0g==", + "requires": { + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-ens": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-ens/-/web3-eth-ens-1.2.1.tgz", + "integrity": "sha512-lhP1kFhqZr2nnbu3CGIFFrAnNxk2veXpOXBY48Tub37RtobDyHijHgrj+xTh+mFiPokyrapVjpFsbGa+Xzye4Q==", + "requires": { + "eth-ens-namehash": "2.0.8", + "underscore": "1.9.1", + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-promievent": "1.2.1", + "web3-eth-abi": "1.2.1", + "web3-eth-contract": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-eth-iban": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-iban/-/web3-eth-iban-1.2.1.tgz", + "integrity": "sha512-9gkr4QPl1jCU+wkgmZ8EwODVO3ovVj6d6JKMos52ggdT2YCmlfvFVF6wlGLwi0VvNa/p+0BjJzaqxnnG/JewjQ==", + "requires": { + "bn.js": "4.11.8", + "web3-utils": "1.2.1" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + } + } + }, + "web3-eth-personal": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-eth-personal/-/web3-eth-personal-1.2.1.tgz", + "integrity": "sha512-RNDVSiaSoY4aIp8+Hc7z+X72H7lMb3fmAChuSBADoEc7DsJrY/d0R5qQDK9g9t2BO8oxgLrLNyBP/9ub2Hc6Bg==", + "requires": { + "web3-core": "1.2.1", + "web3-core-helpers": "1.2.1", + "web3-core-method": "1.2.1", + "web3-net": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-net": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-net/-/web3-net-1.2.1.tgz", + "integrity": "sha512-Yt1Bs7WgnLESPe0rri/ZoPWzSy55ovioaP35w1KZydrNtQ5Yq4WcrAdhBzcOW7vAkIwrsLQsvA+hrOCy7mNauw==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-utils": "1.2.1" + } + }, + "web3-provider-engine": { + "version": "git+https://github.com/trufflesuite/provider-engine.git#9694f5b4e5500651bd2ff689df8529bb5cf6b96f", + "from": "git+https://github.com/trufflesuite/provider-engine.git#web3-one", + "requires": { + "async": "^2.5.0", + "backoff": "^2.5.0", + "clone": "^2.0.0", + "cross-fetch": "^2.1.0", + "eth-block-tracker": "^4.2.0", + "eth-json-rpc-filters": "^4.0.2", + "eth-json-rpc-infura": "^3.1.0", + "eth-json-rpc-middleware": "^4.1.1", + "eth-sig-util": "^1.4.2", + "ethereumjs-block": "^1.2.2", + "ethereumjs-tx": "^1.2.0", + "ethereumjs-util": "^5.1.5", + "ethereumjs-vm": "^2.3.4", + "json-rpc-error": "^2.0.0", + "json-stable-stringify": "^1.0.1", + "promise-to-callback": "^1.0.0", + "readable-stream": "^2.2.9", + "request": "^2.85.0", + "semaphore": "^1.0.3", + "ws": "^5.1.1", + "xhr": "^2.2.0", + "xtend": "^4.0.1" + }, + "dependencies": { + "ethereumjs-util": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/ethereumjs-util/-/ethereumjs-util-5.2.1.tgz", + "integrity": "sha512-v3kT+7zdyCm1HIqWlLNrHGqHGLpGYIhjeHxQjnDXjLT2FyGJDsd3LWMYUo7pAFRrk86CR3nUJfhC81CCoJNNGQ==", + "requires": { + "bn.js": "^4.11.0", + "create-hash": "^1.1.2", + "elliptic": "^6.5.2", + "ethereum-cryptography": "^0.1.3", + "ethjs-util": "^0.1.3", + "rlp": "^2.0.0", + "safe-buffer": "^5.1.1" + } + }, + "readable-stream": { + "version": "2.3.7", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", + "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", + "requires": { + "core-util-is": "~1.0.0", + "inherits": "~2.0.3", + "isarray": "~1.0.0", + "process-nextick-args": "~2.0.0", + "safe-buffer": "~5.1.1", + "string_decoder": "~1.1.1", + "util-deprecate": "~1.0.1" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", + "requires": { + "safe-buffer": "~5.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "ws": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/ws/-/ws-5.2.2.tgz", + "integrity": "sha512-jaHFD6PFv6UgoIVda6qZllptQsMlDEJkTQcybzzXDYM1XO9Y8em691FGMPmM46WGyLU4z9KMgQN+qrux/nhlHA==", + "requires": { + "async-limiter": "~1.0.0" + } + } + } + }, + "web3-providers-http": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-http/-/web3-providers-http-1.2.1.tgz", + "integrity": "sha512-BDtVUVolT9b3CAzeGVA/np1hhn7RPUZ6YYGB/sYky+GjeO311Yoq8SRDUSezU92x8yImSC2B+SMReGhd1zL+bQ==", + "requires": { + "web3-core-helpers": "1.2.1", + "xhr2-cookies": "1.1.0" + } + }, + "web3-providers-ipc": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-ipc/-/web3-providers-ipc-1.2.1.tgz", + "integrity": "sha512-oPEuOCwxVx8L4CPD0TUdnlOUZwGBSRKScCz/Ws2YHdr9Ium+whm+0NLmOZjkjQp5wovQbyBzNa6zJz1noFRvFA==", + "requires": { + "oboe": "2.1.4", + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1" + } + }, + "web3-providers-ws": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-providers-ws/-/web3-providers-ws-1.2.1.tgz", + "integrity": "sha512-oqsQXzu+ejJACVHy864WwIyw+oB21nw/pI65/sD95Zi98+/HQzFfNcIFneF1NC4bVF3VNX4YHTNq2I2o97LAiA==", + "requires": { + "underscore": "1.9.1", + "web3-core-helpers": "1.2.1", + "websocket": "github:web3-js/WebSocket-Node#polyfill/globalThis" + } + }, + "web3-shh": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-shh/-/web3-shh-1.2.1.tgz", + "integrity": "sha512-/3Cl04nza5kuFn25bV3FJWa0s3Vafr5BlT933h26xovQ6HIIz61LmvNQlvX1AhFL+SNJOTcQmK1SM59vcyC8bA==", + "requires": { + "web3-core": "1.2.1", + "web3-core-method": "1.2.1", + "web3-core-subscriptions": "1.2.1", + "web3-net": "1.2.1" + } + }, + "web3-utils": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/web3-utils/-/web3-utils-1.2.1.tgz", + "integrity": "sha512-Mrcn3l58L+yCKz3zBryM6JZpNruWuT0OCbag8w+reeNROSGVlXzUQkU+gtAwc9JCZ7tKUyg67+2YUGqUjVcyBA==", + "requires": { + "bn.js": "4.11.8", + "eth-lib": "0.2.7", + "ethjs-unit": "0.1.6", + "number-to-bn": "1.7.0", + "randomhex": "0.1.5", + "underscore": "1.9.1", + "utf8": "3.0.0" + }, + "dependencies": { + "bn.js": { + "version": "4.11.8", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.11.8.tgz", + "integrity": "sha512-ItfYfPLkWHUjckQCk8xC+LwxgK8NYcXywGigJgSwOP8Y2iyWT4f2vsZnoOXTTbo+o5yXmIUJ4gn5538SO5S3gA==" + }, + "eth-lib": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/eth-lib/-/eth-lib-0.2.7.tgz", + "integrity": "sha1-L5Pxex4jrsN1nNSj/iDBKGo/wco=", + "requires": { + "bn.js": "^4.11.6", + "elliptic": "^6.4.0", + "xhr-request-promise": "^0.1.2" + } + } + } + }, + "websocket": { + "version": "1.0.29", + "resolved": "github:web3-js/WebSocket-Node#ef5ea2f41daf4a2113b80c9223df884b4d56c400", + "requires": { + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "nan": "^2.14.0", + "typedarray-to-buffer": "^3.1.5", + "yaeti": "^0.0.6" + } + }, + "whatwg-fetch": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/whatwg-fetch/-/whatwg-fetch-2.0.4.tgz", + "integrity": "sha512-dcQ1GWpOD/eEQ97k66aiEVpNnapVj90/+R+SXTPYGHpYBBypfKJEQjLrvMZ7YXbKm21gXd4NcuxUTjiv1YtLng==" + }, + "wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=" + }, + "ws": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/ws/-/ws-3.3.3.tgz", + "integrity": "sha512-nnWLa/NwZSt4KQJu51MYlCcSQ5g7INpOrOMt4XV8j4dqTXdmlUmSHQ8/oLC069ckre0fRsgfvsKwbTdtKLCDkA==", + "requires": { + "async-limiter": "~1.0.0", + "safe-buffer": "~5.1.0", + "ultron": "~1.1.0" + }, + "dependencies": { + "safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==" + } + } + }, + "xhr": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/xhr/-/xhr-2.5.0.tgz", + "integrity": "sha512-4nlO/14t3BNUZRXIXfXe+3N6w3s1KoxcJUUURctd64BLRe67E4gRwp4PjywtDY72fXpZ1y6Ch0VZQRY/gMPzzQ==", + "requires": { + "global": "~4.3.0", + "is-function": "^1.0.1", + "parse-headers": "^2.0.0", + "xtend": "^4.0.0" + } + }, + "xhr-request": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr-request/-/xhr-request-1.1.0.tgz", + "integrity": "sha512-Y7qzEaR3FDtL3fP30k9wO/e+FBnBByZeybKOhASsGP30NIkRAAkKD/sCnLvgEfAIEC1rcmK7YG8f4oEnIrrWzA==", + "requires": { + "buffer-to-arraybuffer": "^0.0.5", + "object-assign": "^4.1.1", + "query-string": "^5.0.1", + "simple-get": "^2.7.0", + "timed-out": "^4.0.1", + "url-set-query": "^1.0.0", + "xhr": "^2.0.4" + } + }, + "xhr-request-promise": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/xhr-request-promise/-/xhr-request-promise-0.1.3.tgz", + "integrity": "sha512-YUBytBsuwgitWtdRzXDDkWAXzhdGB8bYm0sSzMPZT7Z2MBjMSTHFsyCT1yCRATY+XC69DUrQraRAEgcoCRaIPg==", + "requires": { + "xhr-request": "^1.1.0" + } + }, + "xhr2-cookies": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/xhr2-cookies/-/xhr2-cookies-1.1.0.tgz", + "integrity": "sha1-fXdEnQmZGX8VXLc7I99yUF7YnUg=", + "requires": { + "cookiejar": "^2.1.1" + } + }, + "xmlhttprequest": { + "version": "1.8.0", + "resolved": "https://registry.npmjs.org/xmlhttprequest/-/xmlhttprequest-1.8.0.tgz", + "integrity": "sha1-Z/4HXFwk/vOfnWX197f+dRcZaPw=" + }, + "xtend": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", + "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==" + }, + "yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha1-8m9ITXJoTPQr7ft2lwqhYI+/lXc=" + }, + "yallist": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", + "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==" + }, + "yauzl": { + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", + "integrity": "sha1-x+sXyT4RLLEIb6bY5R+wZnt5pfk=", + "requires": { + "buffer-crc32": "~0.2.3", + "fd-slicer": "~1.1.0" + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/package.json b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/package.json new file mode 100644 index 0000000000..12e9d197f4 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/package.json @@ -0,0 +1,27 @@ +{ + "name": "truffletest", + "version": "1.0.0", + "description": "", + "main": "index.js", + "directories": { + "test": "test" + }, + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/vicnaum/truffleTest.git" + }, + "author": "", + "license": "ISC", + "bugs": { + "url": "https://github.com/vicnaum/truffleTest/issues" + }, + "homepage": "https://github.com/vicnaum/truffleTest#readme", + "dependencies": { + "@catalyst-net-js/truffle-provider": "0.3.9", + "eth-json-rpc-filters": "^4.1.1", + "truffle-hdwallet-provider": "^1.0.17" + } +} diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/truffle-config.js b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/truffle-config.js new file mode 100644 index 0000000000..3d4132ad0e --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/truffle-config.js @@ -0,0 +1,104 @@ +/** + * Use this file to configure your truffle project. It's seeded with some + * common settings for different networks and features like migrations, + * compilation and testing. Uncomment the ones you need or modify + * them to suit your project as necessary. + * + * More information about configuration can be found at: + * + * truffleframework.com/docs/advanced/configuration + * + * To deploy via Infura you'll need a wallet provider (like truffle-hdwallet-provider) + * to sign your transactions before they're sent to a remote public node. Infura accounts + * are available for free at: infura.io/register. + * + * You'll also need a mnemonic - the twelve word phrase the wallet uses to generate + * public/private key pairs. If you're publishing your code to GitHub make sure you load this + * phrase from a file you've .gitignored so it doesn't accidentally become public. + * + */ + +const {HDWalletProvider} = require("@catalyst-net-js/truffle-provider"); +// const HDWalletProvider = require('truffle-hdwallet-provider'); +// const infuraKey = "fj4jll3k....."; +// +const fs = require('fs'); +const mnemonic = fs.readFileSync(".secret").toString().trim(); + +module.exports = { + /** + * Networks define how you connect to your ethereum client and let you set the + * defaults web3 uses to send transactions. If you don't specify one truffle + * will spin up a development blockchain for you on port 9545 when you + * run `develop` or `test`. You can ask a truffle command to use a specific + * network from the command line, e.g + * + * $ truffle test --network + */ + + networks: { + // Useful for testing. The `development` name is special - truffle uses it by default + // if it's defined here and no other network is specified at the command line. + // You should run a client (like ganache-cli, geth or parity) in a separate terminal + // tab if you use this network and you must also set the `host`, `port` and `network_id` + // options below to some value. + // + development: { + provider: () => new HDWalletProvider(mnemonic, `http://127.0.0.1:8545`), + network_id: "*", // Any network (default: none) + }, + + catalyst: { + provider: () => new HDWalletProvider(mnemonic, `http://127.0.0.1:5005/api/eth/request`), + network_id: "*", // Any network (default: none) + }, + + // Another network with more advanced options... + // advanced: { + // port: 8777, // Custom port + // network_id: 1342, // Custom network + // gas: 8500000, // Gas sent with each transaction (default: ~6700000) + // gasPrice: 20000000000, // 20 gwei (in wei) (default: 100 gwei) + // from:
, // Account to send txs from (default: accounts[0]) + // websockets: true // Enable EventEmitter interface for web3 (default: false) + // }, + + // Useful for deploying to a public network. + // NB: It's important to wrap the provider as a function. + // ropsten: { + // provider: () => new HDWalletProvider(mnemonic, `https://ropsten.infura.io/v3/YOUR-PROJECT-ID`), + // network_id: 3, // Ropsten's id + // gas: 5500000, // Ropsten has a lower block limit than mainnet + // confirmations: 2, // # of confs to wait between deployments. (default: 0) + // timeoutBlocks: 200, // # of blocks before a deployment times out (minimum/default: 50) + // skipDryRun: true // Skip dry run before migrations? (default: false for public nets ) + // }, + + // Useful for private networks + // private: { + // provider: () => new HDWalletProvider(mnemonic, `https://network.io`), + // network_id: 2111, // This network is yours, in the cloud. + // production: true // Treats this network as if it was a public net. (default: false) + // } + }, + + // Set default mocha options here, use special reporters etc. + mocha: { + // timeout: 100000 + }, + + // Configure your compilers + compilers: { + solc: { + version: "0.4.24", // Fetch exact version from solc-bin (default: truffle's version) + // docker: true, // Use "0.5.1" you've installed locally with docker (default: false) + // settings: { // See the solidity docs for advice about optimization and evmVersion + // optimizer: { + // enabled: false, + // runs: 200 + // }, + // evmVersion: "byzantium" + // } + } + } +} diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/truffle-neth_convo.txt b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/truffle-neth_convo.txt new file mode 100644 index 0000000000..d60516db8b --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger.Tests/IntegrationTests/TruffleTest/truffle-neth_convo.txt @@ -0,0 +1,553 @@ +2020-01-17 15:46:31.9880|Nethermind config: +{ + "enableUnsecuredDevWallet": true, + "keepDevWalletInMemory": true, + "webSocketsEnabled": false, + "discoveryEnabled": false, + "processingEnabled": true, + "peerManagerEnabled": false, + "isMining": true, + "chainSpecPath": "chainspec/spaceneth.json", + "chainSpecFormat": "chainspec", + "baseDbPath": "C:\\spaceneth_db", + "logFileName": "spaceneth.logs.txt", + "staticNodesPath": "Data/static-nodes.json", + "storeTraces": false, + "storeReceipts": true, + "useMemDb": true +} +| +2020-01-17 15:46:32.0095|Monitoring is disabled| +2020-01-17 15:46:32.0852|Using http://checkip.amazonaws.com to get external ip| +2020-01-17 15:46:32.3726|External ip: 94.9.101.35 +| +2020-01-17 15:46:32.4197|Loading chain spec from chainspec/spaceneth.json| +2020-01-17 15:46:32.5200|Block tree initialized, last processed is 0, best queued is 0, best known is 0, lowest inserted header , body | +2020-01-17 15:46:32.6076|Genesis hash : 0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79| +2020-01-17 15:46:32.6118|Starting Dev block producer & sealer| +2020-01-17 15:46:32.6118|Found no blocks to load from DB| +2020-01-17 15:46:32.7733|Loading static nodes from file: C:\src\nethermind\src\Nethermind\Nethermind.Runner\Data\static-nodes.json| +2020-01-17 15:46:32.7733|Loaded 0 static nodes.| +2020-01-17 15:46:32.7867|Initializing persisted peers: 0.| +2020-01-17 15:46:32.7867|Skipping discovery init due to (DiscoveryEnabled set to false)| +2020-01-17 15:46:32.7867|Skipping peer manager init due to PeerManagerEnabled set to false)| +2020-01-17 15:46:32.7998|Ethereum : tcp://94.9.101.35:30303| +2020-01-17 15:46:32.7998|Version : Nethermind/v1.4.8-7-db2c41ab0-20200117/X64-Microsoft Windows 10.0.18362/Core3.0.0| +2020-01-17 15:46:32.7998|This node : enode://9dbb44163c179cd31c6b5579db6320843abb088254528f464dcbc5122fae956bf2e085b2dea195e7581180daae81835e6ab888c62c5669764c993e8190b90077@94.9.101.35:30303| +2020-01-17 15:46:32.7998|Node address : 0x74b79e3b68a43454e11a4ceae96f3594e459e9bf (do not use as an account)| +2020-01-17 15:46:32.8602|ETH Stats integration is disabled.| +2020-01-17 15:46:32.8602|Running server, url: http://127.0.0.1:8545| +2020-01-17 15:46:33.2479|JSON RPC : http://127.0.0.1:8545| +2020-01-17 15:46:33.2532|RPC modules : Admin, Debug, Eth, Net, Parity, Personal, Trace, TxPool, Web3| +2020-01-17 15:46:33.6344|Waiting for useful peers in 'DbSync' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:33.6344|Sync peers - Initialized: 0 | All: 0 | Max: 50| +2020-01-17 15:46:33.6344|Sync peers 0(0)/50, searching for peers to sync with...| +2020-01-17 15:46:33.8003|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:34.3485|Executing JSON RPC call web3_clientversion with params | +2020-01-17 15:46:34.3485|Sending JSON RPC response: {"id":67,"jsonrpc":"2.0","result":"Nethermind/v1.4.8-7-db2c41ab0-20200117/X64-Microsoft Windows 10.0.18362/Core3.0.0"}| +2020-01-17 15:46:34.6513|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:34.8015|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:35.6561|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:35.8068|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:36.6673|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:36.8140|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:37.6772|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:37.8211|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:38.6903|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:38.8311|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:39.7031|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:39.8322|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:40.7060|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:40.8375|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:41.7083|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:41.8432|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:42.7209|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:42.8432|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:43.7278|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:43.8440|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:44.7339|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:44.8450|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:45.7379|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:45.8459|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:45.9503|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:45.9503|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:45.9889|Sending JSON RPC response: {"id":8728138322692918,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:45.9889|Sending JSON RPC response: {"id":8728138322692917,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:46.0174|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:46.0174|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:46.0174|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:46.0174|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:46.0174|Sending JSON RPC response: {"id":8728138322692920,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:46.0174|Sending JSON RPC response: {"id":8728138322692919,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:46.0174|Sending JSON RPC response: {"id":8728138322692922,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:46.0174|Sending JSON RPC response: {"id":8728138322692921,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:46.7380|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:46.8469|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:47.7507|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:47.8598|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:48.7548|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:48.8628|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:49.2476|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:49.2476|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:49.2476|Sending JSON RPC response: {"id":8728138322692924,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:49.2476|Sending JSON RPC response: {"id":8728138322692923,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:49.3421|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:49.3421|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:49.3421|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:49.3421|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:49.3421|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":8728138322692929,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":8728138322692926,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":8728138322692928,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":8728138322692925,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":8728138322692927,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:49.3429|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:49.3429|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:49.3429|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:49.3429|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":8728138322692932,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":8728138322692933,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":8728138322692931,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":8728138322692930,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:49.3429|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:49.3429|Executing JSON RPC call eth_blocknumber with params | +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":8728138322692934,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":1,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:49.3429|Executing JSON RPC call net_version with params | +2020-01-17 15:46:49.3429|Sending JSON RPC response: {"id":2,"jsonrpc":"2.0","result":"99"}| +2020-01-17 15:46:49.3891|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:49.3891|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:49.3891|Sending JSON RPC response: {"id":8728138322692936,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:49.3891|Sending JSON RPC response: {"id":8728138322692935,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:49.3925|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:49.3925|Sending JSON RPC response: {"id":8728138322692937,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:49.3925|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:49.3925|Sending JSON RPC response: {"id":8728138322692938,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:49.3925|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:49.3925|Sending JSON RPC response: {"id":5,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:49.6274|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:49.6274|Sending JSON RPC response: {"id":8728138322692939,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:49.6274|Executing JSON RPC call net_version with params | +2020-01-17 15:46:49.6274|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:49.6274|Sending JSON RPC response: {"id":1,"jsonrpc":"2.0","result":"99"}| +2020-01-17 15:46:49.6274|Sending JSON RPC response: {"id":8728138322692940,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:49.6683|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:49.6683|Sending JSON RPC response: {"id":2,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:49.7626|Executing JSON RPC call eth_getcode with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:49.7681|Sending JSON RPC response: {"id":3,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:49.7681|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:49.7947|Executing JSON RPC call eth_getcode with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:49.7947|Sending JSON RPC response: {"id":4,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:49.8647|Executing JSON RPC call eth_getcode with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:49.8647|Executing JSON RPC call eth_gettransactioncount with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:49.8647|Executing JSON RPC call eth_getbalance with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:49.8647|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:49.8647|Sending JSON RPC response: {"id":5,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:49.8647|Sending JSON RPC response: {"id":7,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:49.8647|Sending JSON RPC response: {"id":6,"jsonrpc":"2.0","result":"0xd3c21bcecceda1000000"}| +2020-01-17 15:46:49.8852|Executing JSON RPC call eth_getcode with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:49.8852|Executing JSON RPC call eth_gettransactioncount with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:49.8852|Executing JSON RPC call eth_getbalance with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:49.8852|Sending JSON RPC response: {"id":8,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:49.8852|Sending JSON RPC response: {"id":10,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:49.8852|Sending JSON RPC response: {"id":9,"jsonrpc":"2.0","result":"0xd3c21bcecceda1000000"}| +2020-01-17 15:46:49.8953|Executing JSON RPC call eth_gettransactioncount with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:49.8953|Executing JSON RPC call eth_getbalance with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:49.8953|Executing JSON RPC call eth_getcode with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:49.8953|Sending JSON RPC response: {"id":11,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:49.8953|Sending JSON RPC response: {"id":13,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:49.8953|Sending JSON RPC response: {"id":12,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:49.8953|Executing JSON RPC call eth_getbalance with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:49.8953|Executing JSON RPC call eth_getcode with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:49.8953|Executing JSON RPC call eth_gettransactioncount with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:49.8953|Sending JSON RPC response: {"id":14,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:49.8953|Sending JSON RPC response: {"id":16,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:49.8953|Sending JSON RPC response: {"id":15,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:49.9107|Executing JSON RPC call eth_getstorageat with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0,0x0| +2020-01-17 15:46:49.9107|Sending JSON RPC response: {"id":17,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:49.9217|Executing JSON RPC call eth_getstorageat with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0,0x0| +2020-01-17 15:46:49.9217|Sending JSON RPC response: {"id":18,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:49.9217|Executing JSON RPC call eth_getcode with params 0x0000000000000000000000000000000000000000,0x0| +2020-01-17 15:46:49.9217|Sending JSON RPC response: {"id":19,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:49.9217|Executing JSON RPC call eth_getbalance with params 0x0000000000000000000000000000000000000000,0x0| +2020-01-17 15:46:49.9217|Sending JSON RPC response: {"id":20,"jsonrpc":"2.0","result":"0x1"}| +2020-01-17 15:46:49.9217|Executing JSON RPC call eth_gettransactioncount with params 0x0000000000000000000000000000000000000000,0x0| +2020-01-17 15:46:49.9217|Sending JSON RPC response: {"id":21,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:50.7817|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:50.8798|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:51.5777|Executing JSON RPC call eth_getcode with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.5777|Executing JSON RPC call eth_getbalance with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.5777|Executing JSON RPC call eth_gettransactioncount with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.5777|Sending JSON RPC response: {"id":23,"jsonrpc":"2.0","result":"0xd3c21bcecceda1000000"}| +2020-01-17 15:46:51.5777|Sending JSON RPC response: {"id":24,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.5777|Sending JSON RPC response: {"id":22,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.5777|Executing JSON RPC call eth_getcode with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.5777|Executing JSON RPC call eth_getbalance with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.5777|Executing JSON RPC call eth_gettransactioncount with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.5777|Sending JSON RPC response: {"id":25,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.5777|Sending JSON RPC response: {"id":26,"jsonrpc":"2.0","result":"0xd3c21bcecceda1000000"}| +2020-01-17 15:46:51.5777|Sending JSON RPC response: {"id":27,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.5777|Executing JSON RPC call eth_getcode with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.5777|Executing JSON RPC call eth_getbalance with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.5777|Executing JSON RPC call eth_gettransactioncount with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.5777|Sending JSON RPC response: {"id":28,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.5777|Sending JSON RPC response: {"id":30,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.5777|Sending JSON RPC response: {"id":29,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.5974|Executing JSON RPC call eth_gettransactioncount with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.5974|Executing JSON RPC call eth_getcode with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.5974|Executing JSON RPC call eth_getbalance with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.5974|Sending JSON RPC response: {"id":31,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.5974|Sending JSON RPC response: {"id":32,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.5974|Sending JSON RPC response: {"id":33,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.5974|Executing JSON RPC call eth_getstorageat with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0,0x0| +2020-01-17 15:46:51.5974|Sending JSON RPC response: {"id":34,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.5974|Executing JSON RPC call eth_getstorageat with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0,0x0| +2020-01-17 15:46:51.5974|Sending JSON RPC response: {"id":35,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.6172|Executing JSON RPC call eth_getcode with params 0x0000000000000000000000000000000000000000,0x0| +2020-01-17 15:46:51.6172|Executing JSON RPC call eth_gettransactioncount with params 0x0000000000000000000000000000000000000000,0x0| +2020-01-17 15:46:51.6172|Executing JSON RPC call eth_getbalance with params 0x0000000000000000000000000000000000000000,0x0| +2020-01-17 15:46:51.6172|Sending JSON RPC response: {"id":36,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.6172|Sending JSON RPC response: {"id":37,"jsonrpc":"2.0","result":"0x1"}| +2020-01-17 15:46:51.6172|Sending JSON RPC response: {"id":38,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.6415|Executing JSON RPC call eth_gettransactioncount with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.6415|Executing JSON RPC call eth_getcode with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.6415|Executing JSON RPC call eth_getbalance with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.6415|Sending JSON RPC response: {"id":40,"jsonrpc":"2.0","result":"0xd3c21bcecceda1000000"}| +2020-01-17 15:46:51.6415|Sending JSON RPC response: {"id":41,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.6415|Sending JSON RPC response: {"id":39,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.6653|Executing JSON RPC call eth_gettransactioncount with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.6653|Executing JSON RPC call eth_getcode with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.6653|Executing JSON RPC call eth_getbalance with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,0x0| +2020-01-17 15:46:51.6653|Sending JSON RPC response: {"id":44,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.6653|Sending JSON RPC response: {"id":42,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.6653|Sending JSON RPC response: {"id":43,"jsonrpc":"2.0","result":"0xd3c21bcecceda1000000"}| +2020-01-17 15:46:51.6734|Executing JSON RPC call eth_getcode with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.6734|Executing JSON RPC call eth_getbalance with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.6734|Executing JSON RPC call eth_gettransactioncount with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.6734|Sending JSON RPC response: {"id":45,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.6734|Sending JSON RPC response: {"id":46,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.6734|Sending JSON RPC response: {"id":47,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.6734|Executing JSON RPC call eth_getcode with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.6734|Executing JSON RPC call eth_getbalance with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.6734|Executing JSON RPC call eth_gettransactioncount with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0| +2020-01-17 15:46:51.6734|Sending JSON RPC response: {"id":48,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.6734|Sending JSON RPC response: {"id":49,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.6734|Sending JSON RPC response: {"id":50,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.6734|Executing JSON RPC call eth_getstorageat with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0,0x0| +2020-01-17 15:46:51.6734|Sending JSON RPC response: {"id":51,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.6907|Executing JSON RPC call eth_getstorageat with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x0,0x0| +2020-01-17 15:46:51.6907|Sending JSON RPC response: {"id":52,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.6907|Executing JSON RPC call eth_getcode with params 0x0000000000000000000000000000000000000000,0x0| +2020-01-17 15:46:51.6907|Executing JSON RPC call eth_getbalance with params 0x0000000000000000000000000000000000000000,0x0| +2020-01-17 15:46:51.6907|Executing JSON RPC call eth_gettransactioncount with params 0x0000000000000000000000000000000000000000,0x0| +2020-01-17 15:46:51.6907|Sending JSON RPC response: {"id":53,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:51.6907|Sending JSON RPC response: {"id":55,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:51.6907|Sending JSON RPC response: {"id":54,"jsonrpc":"2.0","result":"0x1"}| +2020-01-17 15:46:51.9670|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:51.9681|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:52.0262|Executing JSON RPC call eth_getstorageat with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x1,0x0| +2020-01-17 15:46:52.0262|Sending JSON RPC response: {"id":56,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.0737|Executing JSON RPC call eth_getstorageat with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,0x1,0x0| +2020-01-17 15:46:52.0737|Sending JSON RPC response: {"id":57,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.2180|Executing JSON RPC call eth_getcode with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.2180|Sending JSON RPC response: {"id":58,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.2180|Executing JSON RPC call eth_getbalance with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.2180|Executing JSON RPC call eth_gettransactioncount with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.2180|Sending JSON RPC response: {"id":59,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.2180|Sending JSON RPC response: {"id":60,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.2180|Executing JSON RPC call eth_getcode with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.2180|Executing JSON RPC call eth_getbalance with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.2180|Executing JSON RPC call eth_gettransactioncount with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.2180|Sending JSON RPC response: {"id":63,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.2180|Sending JSON RPC response: {"id":62,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.2180|Sending JSON RPC response: {"id":61,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.2471|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x9557b59d272c7577c2a196e993fe53a09330cfa9c5e07638a1e4adff4b67fb54,0x0| +2020-01-17 15:46:52.2471|Sending JSON RPC response: {"id":64,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.2550|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0,0x0| +2020-01-17 15:46:52.2550|Sending JSON RPC response: {"id":65,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.2651|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x3,0x0| +2020-01-17 15:46:52.2651|Sending JSON RPC response: {"id":66,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.2651|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x3,0x0| +2020-01-17 15:46:52.2651|Sending JSON RPC response: {"id":67,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.2911|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x4,0x0| +2020-01-17 15:46:52.2911|Sending JSON RPC response: {"id":68,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.3449|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x4,0x0| +2020-01-17 15:46:52.3449|Sending JSON RPC response: {"id":69,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.3449|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x5,0x0| +2020-01-17 15:46:52.3449|Sending JSON RPC response: {"id":70,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.3639|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x5,0x0| +2020-01-17 15:46:52.3639|Sending JSON RPC response: {"id":71,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.4227|Executing JSON RPC call eth_gettransactioncount with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.4227|Executing JSON RPC call eth_getbalance with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.4227|Executing JSON RPC call eth_getcode with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.4227|Sending JSON RPC response: {"id":72,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.4227|Sending JSON RPC response: {"id":73,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.4227|Sending JSON RPC response: {"id":74,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.4227|Executing JSON RPC call eth_getcode with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.4227|Executing JSON RPC call eth_getbalance with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.4227|Executing JSON RPC call eth_gettransactioncount with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.4227|Sending JSON RPC response: {"id":75,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.4227|Sending JSON RPC response: {"id":76,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.4227|Sending JSON RPC response: {"id":77,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.4438|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x9557b59d272c7577c2a196e993fe53a09330cfa9c5e07638a1e4adff4b67fb54,0x0| +2020-01-17 15:46:52.4438|Sending JSON RPC response: {"id":78,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.4438|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0,0x0| +2020-01-17 15:46:52.4438|Sending JSON RPC response: {"id":79,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.4597|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x3,0x0| +2020-01-17 15:46:52.4597|Sending JSON RPC response: {"id":80,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.4597|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x3,0x0| +2020-01-17 15:46:52.4597|Sending JSON RPC response: {"id":81,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.4749|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x4,0x0| +2020-01-17 15:46:52.4749|Sending JSON RPC response: {"id":82,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.4749|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x4,0x0| +2020-01-17 15:46:52.4749|Sending JSON RPC response: {"id":83,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.4897|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x5,0x0| +2020-01-17 15:46:52.4897|Sending JSON RPC response: {"id":84,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.4897|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x5,0x0| +2020-01-17 15:46:52.4897|Sending JSON RPC response: {"id":85,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.6099|Executing JSON RPC call eth_gettransactioncount with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.6099|Executing JSON RPC call eth_getcode with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.6099|Executing JSON RPC call eth_getbalance with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.6099|Sending JSON RPC response: {"id":87,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.6099|Sending JSON RPC response: {"id":88,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.6099|Sending JSON RPC response: {"id":86,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.6099|Executing JSON RPC call eth_getcode with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.6099|Executing JSON RPC call eth_getbalance with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.6099|Executing JSON RPC call eth_gettransactioncount with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0| +2020-01-17 15:46:52.6099|Sending JSON RPC response: {"id":89,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.6099|Sending JSON RPC response: {"id":90,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.6099|Sending JSON RPC response: {"id":91,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:52.6293|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x9557b59d272c7577c2a196e993fe53a09330cfa9c5e07638a1e4adff4b67fb54,0x0| +2020-01-17 15:46:52.6293|Sending JSON RPC response: {"id":92,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.6293|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x0,0x0| +2020-01-17 15:46:52.6293|Sending JSON RPC response: {"id":93,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.6413|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x3,0x0| +2020-01-17 15:46:52.6413|Sending JSON RPC response: {"id":94,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.6413|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x3,0x0| +2020-01-17 15:46:52.6413|Sending JSON RPC response: {"id":95,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.6570|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x4,0x0| +2020-01-17 15:46:52.6570|Sending JSON RPC response: {"id":96,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.6570|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x4,0x0| +2020-01-17 15:46:52.6570|Sending JSON RPC response: {"id":97,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.7345|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x5,0x0| +2020-01-17 15:46:52.7345|Sending JSON RPC response: {"id":98,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.7345|Executing JSON RPC call eth_getstorageat with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,0x5,0x0| +2020-01-17 15:46:52.7345|Sending JSON RPC response: {"id":99,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:52.9727|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:52.9727|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:53.0508|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.0508|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.0508|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.0508|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.0508|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.0508|Sending JSON RPC response: {"id":8728138322692941,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.0508|Sending JSON RPC response: {"id":8728138322692944,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.0508|Sending JSON RPC response: {"id":8728138322692943,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.0508|Sending JSON RPC response: {"id":8728138322692945,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.0508|Sending JSON RPC response: {"id":8728138322692942,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.0508|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.0508|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.0508|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.0508|Sending JSON RPC response: {"id":8728138322692946,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.0508|Executing JSON RPC call eth_blocknumber with params | +2020-01-17 15:46:53.0508|Sending JSON RPC response: {"id":8728138322692948,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.0508|Sending JSON RPC response: {"id":8728138322692947,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.0508|Sending JSON RPC response: {"id":58,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:53.0508|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.0508|Sending JSON RPC response: {"id":8728138322692949,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.0508|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.0508|Sending JSON RPC response: {"id":8728138322692950,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.0659|Executing JSON RPC call net_version with params | +2020-01-17 15:46:53.0659|Sending JSON RPC response: {"id":59,"jsonrpc":"2.0","result":"99"}| +2020-01-17 15:46:53.1085|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.1085|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.1091|Sending JSON RPC response: {"id":8728138322692952,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.1091|Sending JSON RPC response: {"id":8728138322692951,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.1091|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.1091|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:53.1091|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.1091|Sending JSON RPC response: {"id":8728138322692953,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.1091|Sending JSON RPC response: {"id":8728138322692954,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.1091|Sending JSON RPC response: {"id":60,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.1091|Executing JSON RPC call eth_getcode with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,latest| +2020-01-17 15:46:53.1091|Sending JSON RPC response: {"id":61,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:53.1844|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.1844|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.1844|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.1844|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.1844|Sending JSON RPC response: {"id":8728138322692958,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.1844|Sending JSON RPC response: {"id":8728138322692956,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.1844|Sending JSON RPC response: {"id":8728138322692955,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.1844|Sending JSON RPC response: {"id":8728138322692957,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.1939|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.1939|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.1939|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:53.1939|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.1939|Sending JSON RPC response: {"id":8728138322692959,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.1939|Sending JSON RPC response: {"id":8728138322692960,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.1939|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.1939|Sending JSON RPC response: {"id":8728138322692961,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.1939|Sending JSON RPC response: {"id":8728138322692962,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.1939|Sending JSON RPC response: {"id":62,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.1939|Executing JSON RPC call eth_getcode with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,latest| +2020-01-17 15:46:53.1939|Sending JSON RPC response: {"id":63,"jsonrpc":"2.0","result":"0x"}| +2020-01-17 15:46:53.2402|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.2402|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.2402|Sending JSON RPC response: {"id":8728138322692964,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.2402|Sending JSON RPC response: {"id":8728138322692963,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.2402|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.2402|Sending JSON RPC response: {"id":8728138322692965,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.2402|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:53.2402|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.2402|Sending JSON RPC response: {"id":8728138322692966,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.2402|Sending JSON RPC response: {"id":64,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.2402|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.2402|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.2402|Sending JSON RPC response: {"id":8728138322692968,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.2402|Sending JSON RPC response: {"id":8728138322692967,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.2940|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.2940|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:53.2940|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:53.2940|Sending JSON RPC response: {"id":8728138322692969,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.2940|Sending JSON RPC response: {"id":66,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.2940|Sending JSON RPC response: {"id":8728138322692970,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.3001|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:53.3001|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.3001|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:53.3001|Sending JSON RPC response: {"id":8728138322692971,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.3001|Sending JSON RPC response: {"id":8728138322692972,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:53.3001|Sending JSON RPC response: {"id":67,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.3001|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:53.3001|Sending JSON RPC response: {"id":68,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:53.3192|Executing JSON RPC call eth_estimategas with params { + "from": "0xb77aec9f59f9d6f39793289a09aea871932619ed", + "gas": "0x6691b7", + "gasPrice": "0x4a817c800", + "data": "0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102f8806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a72305820f2c500de04a8b51feaa58dcde0637ddf187af5ed07336b373801e711033faf180029" +}| +2020-01-17 15:46:53.4366|Sending JSON RPC response: {"id":69,"jsonrpc":"2.0","result":"0x43b96"}| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":8728138322692973,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":8728138322692974,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":8728138322692980,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:54.0367|Waiting for useful peers in 'NotStarted' sync mode | Waiting for peers to learn about the head number.| +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":8728138322692981,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.0367|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":8728138322692975,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":8728138322692979,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":8728138322692976,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":8728138322692978,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":8728138322692977,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_getblockbynumber with params 0x1,True| +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":8728138322692982,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":70,"jsonrpc":"2.0","result":{"author":"0x0000000000000000000000000000000000000000","difficulty":"0x1","extraData":"0x0000000000000000000000000000000000000000000000000000000000000000","gasLimit":"0x1000000","gasUsed":"0x0","hash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":"0x0000000000000000000000000000000000000000000000000000000000000000","nonce":"0x0000000000000042","number":"0x0","parentHash":"0x0000000000000000000000000000000000000000000000000000000000000000","receiptsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x219","stateRoot":"0x069726cffa48edabaeb5bee1341f55b1bae126c86c78619fa5bd4e991af91d14","step":null,"totalDifficulty":"0x1","timestamp":"0x0","transactions":[],"transactionsRoot":"0x56e81f171bcc55a6ff8345e692c0f86e5b48e01b996cadc001622fb5e363b421","uncles":[]}}| +2020-01-17 15:46:54.0367|Executing JSON RPC call eth_blocknumber with params | +2020-01-17 15:46:54.0367|Sending JSON RPC response: {"id":71,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:54.2239|Executing JSON RPC call eth_gettransactioncount with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,pending| +2020-01-17 15:46:54.2239|Sending JSON RPC response: {"id":1579276014048009,"jsonrpc":"2.0","result":"0x0"}| +2020-01-17 15:46:54.2408|Executing JSON RPC call eth_sendrawtransaction with params 0xf903ab808504a817c800836691b78080b90358608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102f8806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a72305820f2c500de04a8b51feaa58dcde0637ddf187af5ed07336b373801e711033faf1800291ba0a31b996b97a460f545c21c317099e2944cada7d6b26651b72490496453becd0aa01acce314132e890abf6b2427a85a51b6e89d7bdca4714f71e22e362214dc0f9c| +2020-01-17 15:46:54.2581|Broadcasting own transaction 0xec3d039f4c30a09e739bc87c924d7265d2848b6077eeae5fa71304ea82c52c9f to 0 peers| +2020-01-17 15:46:54.2881|Sealed block 1 (0x8c6f6d...a2ec48), diff: 1, tx count: 1| +2020-01-17 15:46:54.2881|Sending JSON RPC response: {"id":1579276014237901,"jsonrpc":"2.0","result":"0xec3d039f4c30a09e739bc87c924d7265d2848b6077eeae5fa71304ea82c52c9f"}| +2020-01-17 15:46:54.3047|Full Sync | Processed 1 | 21,756ms, mgasps 0.01 total 0.01, tps 0.05 total 0.05, bps 0.00 total 0.00, recv queue 0, proc queue 0| +2020-01-17 15:46:54.3047|Executing JSON RPC call eth_gettransactionreceipt with params 0xec3d039f4c30a09e739bc87c924d7265d2848b6077eeae5fa71304ea82c52c9f| +2020-01-17 15:46:54.3187|Sending JSON RPC response: {"id":73,"jsonrpc":"2.0","result":{"transactionHash":"0xec3d039f4c30a09e739bc87c924d7265d2848b6077eeae5fa71304ea82c52c9f","transactionIndex":"0x0","blockHash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","blockNumber":"0x1","cumulativeGasUsed":"0x43b96","gasUsed":"0x43b96","from":"0xb77aec9f59f9d6f39793289a09aea871932619ed","to":null,"contractAddress":"0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":null,"status":"0x1","error":""}}| +2020-01-17 15:46:54.3187|Executing JSON RPC call eth_getcode with params 0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f,latest| +2020-01-17 15:46:54.3187|Sending JSON RPC response: {"id":74,"jsonrpc":"2.0","result":"0x608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a72305820f2c500de04a8b51feaa58dcde0637ddf187af5ed07336b373801e711033faf180029"}| +2020-01-17 15:46:54.3187|Executing JSON RPC call eth_gettransactionbyhash with params 0xec3d039f4c30a09e739bc87c924d7265d2848b6077eeae5fa71304ea82c52c9f| +2020-01-17 15:46:54.3334|Sending JSON RPC response: {"id":75,"jsonrpc":"2.0","result":{"hash":"0xec3d039f4c30a09e739bc87c924d7265d2848b6077eeae5fa71304ea82c52c9f","nonce":"0x0","blockHash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","blockNumber":"0x1","transactionIndex":0,"from":"0xb77aec9f59f9d6f39793289a09aea871932619ed","to":null,"value":"0x0","gasPrice":"0x4a817c800","gas":"0x6691b7","data":"0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102f8806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a72305820f2c500de04a8b51feaa58dcde0637ddf187af5ed07336b373801e711033faf180029","input":"0x608060405234801561001057600080fd5b50336000806101000a81548173ffffffffffffffffffffffffffffffffffffffff021916908373ffffffffffffffffffffffffffffffffffffffff1602179055506102f8806100606000396000f300608060405260043610610062576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff1680630900f01014610067578063445df0ac146100aa5780638da5cb5b146100d5578063fdacd5761461012c575b600080fd5b34801561007357600080fd5b506100a8600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610159565b005b3480156100b657600080fd5b506100bf610241565b6040518082815260200191505060405180910390f35b3480156100e157600080fd5b506100ea610247565b604051808273ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200191505060405180910390f35b34801561013857600080fd5b506101576004803603810190808035906020019092919050505061026c565b005b60008060009054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff16141561023d578190508073ffffffffffffffffffffffffffffffffffffffff1663fdacd5766001546040518263ffffffff167c010000000000000000000000000000000000000000000000000000000002815260040180828152602001915050600060405180830381600087803b15801561022457600080fd5b505af1158015610238573d6000803e3d6000fd5b505050505b5050565b60015481565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1681565b6000809054906101000a900473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff1614156102c957806001819055505b505600a165627a7a72305820f2c500de04a8b51feaa58dcde0637ddf187af5ed07336b373801e711033faf180029","v":"0x1b","s":"0x1acce314132e890abf6b2427a85a51b6e89d7bdca4714f71e22e362214dc0f9c","r":"0xa31b996b97a460f545c21c317099e2944cada7d6b26651b72490496453becd0a"}}| +2020-01-17 15:46:54.3334|Executing JSON RPC call eth_getblockbynumber with params 0x1,False| +2020-01-17 15:46:54.3334|Sending JSON RPC response: {"id":76,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0x43b96","hash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x1","parentHash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","receiptsRoot":"0xb4b206e85259edb01f7695b4553249a0a03a376e9a56966ee94ba1ebb74557a3","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x59a","stateRoot":"0x2b765decd3ee1e6e6d70a434897e8dd5144d58059532dc509b53c0f7ab387cd0","step":null,"totalDifficulty":"0x2","timestamp":"0x5e21d6ee","transactions":["0xec3d039f4c30a09e739bc87c924d7265d2848b6077eeae5fa71304ea82c52c9f"],"transactionsRoot":"0xa0509b106a7ba8a42d7be865cd91e806ab94615f2e87dabec2563d3583fa878c","uncles":[]}}| +2020-01-17 15:46:54.3334|Executing JSON RPC call eth_getbalance with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,latest| +2020-01-17 15:46:54.3334|Sending JSON RPC response: {"id":77,"jsonrpc":"2.0","result":"0xd3c21bbb171687f8d000"}| +2020-01-17 15:46:54.3553|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:54.3553|Sending JSON RPC response: {"id":78,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0x43b96","hash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x1","parentHash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","receiptsRoot":"0xb4b206e85259edb01f7695b4553249a0a03a376e9a56966ee94ba1ebb74557a3","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x59a","stateRoot":"0x2b765decd3ee1e6e6d70a434897e8dd5144d58059532dc509b53c0f7ab387cd0","step":null,"totalDifficulty":"0x2","timestamp":"0x5e21d6ee","transactions":["0xec3d039f4c30a09e739bc87c924d7265d2848b6077eeae5fa71304ea82c52c9f"],"transactionsRoot":"0xa0509b106a7ba8a42d7be865cd91e806ab94615f2e87dabec2563d3583fa878c","uncles":[]}}| +2020-01-17 15:46:54.3591|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:54.3591|Sending JSON RPC response: {"id":79,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0x43b96","hash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x1","parentHash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","receiptsRoot":"0xb4b206e85259edb01f7695b4553249a0a03a376e9a56966ee94ba1ebb74557a3","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x59a","stateRoot":"0x2b765decd3ee1e6e6d70a434897e8dd5144d58059532dc509b53c0f7ab387cd0","step":null,"totalDifficulty":"0x2","timestamp":"0x5e21d6ee","transactions":["0xec3d039f4c30a09e739bc87c924d7265d2848b6077eeae5fa71304ea82c52c9f"],"transactionsRoot":"0xa0509b106a7ba8a42d7be865cd91e806ab94615f2e87dabec2563d3583fa878c","uncles":[]}}| +2020-01-17 15:46:54.3591|Executing JSON RPC call eth_sendrawtransaction with params 0xf889018504a817c800836691b7949bced1be5c820ccb4bb52c1f83862d06b6d02c9f80a4fdacd57600000000000000000000000000000000000000000000000000000000000000011ba0a070c2d548d04feadbbafae6aaacb3b6de263527e7bb5dcadab1acc30bd9acfda04af279b14e0792c20a2791296697129586e80e24e0f96ee1103b187b7dd60bd0| +2020-01-17 15:46:54.3591|Broadcasting own transaction 0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17 to 0 peers| +2020-01-17 15:46:54.3591|Sealed block 2 (0x6bd8ca...f16c3c), diff: 1, tx count: 1| +2020-01-17 15:46:54.3591|Sending JSON RPC response: {"id":1579276014365607,"jsonrpc":"2.0","result":"0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17"}| +2020-01-17 15:46:54.3810|Executing JSON RPC call eth_gettransactionreceipt with params 0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17| +2020-01-17 15:46:54.3810|Sending JSON RPC response: {"id":81,"jsonrpc":"2.0","result":{"transactionHash":"0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17","transactionIndex":"0x0","blockHash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","blockNumber":"0x2","cumulativeGasUsed":"0xa418","gasUsed":"0xa418","from":"0xb77aec9f59f9d6f39793289a09aea871932619ed","to":"0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f","contractAddress":null,"logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":null,"status":"0x1","error":""}}| +2020-01-17 15:46:54.3918|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:54.3918|Sending JSON RPC response: {"id":82,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xa418","hash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x2","parentHash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","receiptsRoot":"0x5834bf566ed3a6417b53d443cd8372255e32169894ef8bfde24b52bd5cf5bc8f","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x275","stateRoot":"0xcf25c8fc7fe6303f04ceefa1bb948b86089e0eacf0f4f3e2d2fbc671e263e702","step":null,"totalDifficulty":"0x3","timestamp":"0x5e21d6ef","transactions":["0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17"],"transactionsRoot":"0x693b3ea6f9a5410027ff48c3c9d2e0d66199e5d4cafa81fec6828c9434e84121","uncles":[]}}| +2020-01-17 15:46:54.4399|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:54.4399|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:54.4399|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:54.4399|Sending JSON RPC response: {"id":84,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xa418","hash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x2","parentHash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","receiptsRoot":"0x5834bf566ed3a6417b53d443cd8372255e32169894ef8bfde24b52bd5cf5bc8f","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x275","stateRoot":"0xcf25c8fc7fe6303f04ceefa1bb948b86089e0eacf0f4f3e2d2fbc671e263e702","step":null,"totalDifficulty":"0x3","timestamp":"0x5e21d6ef","transactions":["0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17"],"transactionsRoot":"0x693b3ea6f9a5410027ff48c3c9d2e0d66199e5d4cafa81fec6828c9434e84121","uncles":[]}}| +2020-01-17 15:46:54.4399|Sending JSON RPC response: {"id":8728138322692983,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xa418","hash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x2","parentHash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","receiptsRoot":"0x5834bf566ed3a6417b53d443cd8372255e32169894ef8bfde24b52bd5cf5bc8f","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x275","stateRoot":"0xcf25c8fc7fe6303f04ceefa1bb948b86089e0eacf0f4f3e2d2fbc671e263e702","step":null,"totalDifficulty":"0x3","timestamp":"0x5e21d6ef","transactions":[{"hash":"0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17","nonce":"0x1","blockHash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","blockNumber":"0x2","transactionIndex":0,"from":"0xb77aec9f59f9d6f39793289a09aea871932619ed","to":"0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f","value":"0x0","gasPrice":"0x4a817c800","gas":"0x6691b7","data":"0xfdacd5760000000000000000000000000000000000000000000000000000000000000001","input":"0xfdacd5760000000000000000000000000000000000000000000000000000000000000001","v":"0x1b","s":"0x4af279b14e0792c20a2791296697129586e80e24e0f96ee1103b187b7dd60bd0","r":"0xa070c2d548d04feadbbafae6aaacb3b6de263527e7bb5dcadab1acc30bd9acfd"}],"transactionsRoot":"0x693b3ea6f9a5410027ff48c3c9d2e0d66199e5d4cafa81fec6828c9434e84121","uncles":[]}}| +2020-01-17 15:46:54.4399|Sending JSON RPC response: {"id":8728138322692984,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xa418","hash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x2","parentHash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","receiptsRoot":"0x5834bf566ed3a6417b53d443cd8372255e32169894ef8bfde24b52bd5cf5bc8f","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x275","stateRoot":"0xcf25c8fc7fe6303f04ceefa1bb948b86089e0eacf0f4f3e2d2fbc671e263e702","step":null,"totalDifficulty":"0x3","timestamp":"0x5e21d6ef","transactions":[{"hash":"0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17","nonce":"0x1","blockHash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","blockNumber":"0x2","transactionIndex":0,"from":"0xb77aec9f59f9d6f39793289a09aea871932619ed","to":"0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f","value":"0x0","gasPrice":"0x4a817c800","gas":"0x6691b7","data":"0xfdacd5760000000000000000000000000000000000000000000000000000000000000001","input":"0xfdacd5760000000000000000000000000000000000000000000000000000000000000001","v":"0x1b","s":"0x4af279b14e0792c20a2791296697129586e80e24e0f96ee1103b187b7dd60bd0","r":"0xa070c2d548d04feadbbafae6aaacb3b6de263527e7bb5dcadab1acc30bd9acfd"}],"transactionsRoot":"0x693b3ea6f9a5410027ff48c3c9d2e0d66199e5d4cafa81fec6828c9434e84121","uncles":[]}}| +2020-01-17 15:46:54.4399|Executing JSON RPC call eth_getblockbynumber with params 0x3,True| +2020-01-17 15:46:54.4399|Executing JSON RPC call eth_getblockbynumber with params 0x3,True| +2020-01-17 15:46:54.4399|Sending JSON RPC response: {"id":8728138322692985,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.4399|Sending JSON RPC response: {"id":8728138322692986,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.4399|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:54.4399|Sending JSON RPC response: {"id":85,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xa418","hash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x2","parentHash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","receiptsRoot":"0x5834bf566ed3a6417b53d443cd8372255e32169894ef8bfde24b52bd5cf5bc8f","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x275","stateRoot":"0xcf25c8fc7fe6303f04ceefa1bb948b86089e0eacf0f4f3e2d2fbc671e263e702","step":null,"totalDifficulty":"0x3","timestamp":"0x5e21d6ef","transactions":["0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17"],"transactionsRoot":"0x693b3ea6f9a5410027ff48c3c9d2e0d66199e5d4cafa81fec6828c9434e84121","uncles":[]}}| +2020-01-17 15:46:54.4399|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:54.4399|Sending JSON RPC response: {"id":86,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xa418","hash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x2","parentHash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","receiptsRoot":"0x5834bf566ed3a6417b53d443cd8372255e32169894ef8bfde24b52bd5cf5bc8f","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x275","stateRoot":"0xcf25c8fc7fe6303f04ceefa1bb948b86089e0eacf0f4f3e2d2fbc671e263e702","step":null,"totalDifficulty":"0x3","timestamp":"0x5e21d6ef","transactions":["0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17"],"transactionsRoot":"0x693b3ea6f9a5410027ff48c3c9d2e0d66199e5d4cafa81fec6828c9434e84121","uncles":[]}}| +2020-01-17 15:46:54.4551|Executing JSON RPC call eth_estimategas with params { + "from": "0xb77aec9f59f9d6f39793289a09aea871932619ed", + "gas": "0x6691b7", + "gasPrice": "0x4a817c800", + "data": "0x608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a723058203b831dd3fa25ac66f76a9d2acca6410eec2722187308c9fdcc7be9459fc21861002900000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009546573744549503230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054549503230000000000000000000000000000000000000000000000000000000" +}| +2020-01-17 15:46:54.4551|Sending JSON RPC response: {"id":87,"jsonrpc":"2.0","result":"0xfda0f"}| +2020-01-17 15:46:54.4551|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:54.4551|Executing JSON RPC call eth_blocknumber with params | +2020-01-17 15:46:54.4551|Sending JSON RPC response: {"id":89,"jsonrpc":"2.0","result":"0x2"}| +2020-01-17 15:46:54.4551|Sending JSON RPC response: {"id":88,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xa418","hash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x2","parentHash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","receiptsRoot":"0x5834bf566ed3a6417b53d443cd8372255e32169894ef8bfde24b52bd5cf5bc8f","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x275","stateRoot":"0xcf25c8fc7fe6303f04ceefa1bb948b86089e0eacf0f4f3e2d2fbc671e263e702","step":null,"totalDifficulty":"0x3","timestamp":"0x5e21d6ef","transactions":["0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17"],"transactionsRoot":"0x693b3ea6f9a5410027ff48c3c9d2e0d66199e5d4cafa81fec6828c9434e84121","uncles":[]}}| +2020-01-17 15:46:54.4808|Executing JSON RPC call eth_sendrawtransaction with params 0xf90f83028504a817c800836691b78080b90f30608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a723058203b831dd3fa25ac66f76a9d2acca6410eec2722187308c9fdcc7be9459fc21861002900000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c000000000000000000000000000000000000000000000000000000000000000095465737445495032300000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000545495032300000000000000000000000000000000000000000000000000000001ba0198d0cbd90f04d7e7a3f78b3d0b84fb7c795efc336c88f5cc807e5f140835ad0a01ac377eb69f3a3831b11cf9d6db1a236ef9e6285fbedf2a70575391c758157f6| +2020-01-17 15:46:54.4808|Broadcasting own transaction 0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3 to 0 peers| +2020-01-17 15:46:54.4808|Sealed block 3 (0x419519...b722df), diff: 1, tx count: 1| +2020-01-17 15:46:54.4808|Sending JSON RPC response: {"id":1579276014477605,"jsonrpc":"2.0","result":"0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3"}| +2020-01-17 15:46:54.4945|Executing JSON RPC call eth_gettransactionreceipt with params 0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3| +2020-01-17 15:46:54.4945|Sending JSON RPC response: {"id":91,"jsonrpc":"2.0","result":{"transactionHash":"0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3","transactionIndex":"0x0","blockHash":"0x419519b69e444d4f3fa19adc6aa972ef14987dacad571aabcb38ed2f9bb722df","blockNumber":"0x3","cumulativeGasUsed":"0xfda0f","gasUsed":"0xfda0f","from":"0xb77aec9f59f9d6f39793289a09aea871932619ed","to":null,"contractAddress":"0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7","logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":null,"status":"0x1","error":""}}| +2020-01-17 15:46:54.4990|Executing JSON RPC call eth_getcode with params 0x3c0cdef448bf1e325254d0c20a2fdbdbdb5acad7,latest| +2020-01-17 15:46:54.4990|Sending JSON RPC response: {"id":92,"jsonrpc":"2.0","result":"0x6080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a723058203b831dd3fa25ac66f76a9d2acca6410eec2722187308c9fdcc7be9459fc218610029"}| +2020-01-17 15:46:54.4990|Executing JSON RPC call eth_gettransactionbyhash with params 0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3| +2020-01-17 15:46:54.4990|Sending JSON RPC response: {"id":93,"jsonrpc":"2.0","result":{"hash":"0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3","nonce":"0x2","blockHash":"0x419519b69e444d4f3fa19adc6aa972ef14987dacad571aabcb38ed2f9bb722df","blockNumber":"0x3","transactionIndex":0,"from":"0xb77aec9f59f9d6f39793289a09aea871932619ed","to":null,"value":"0x0","gasPrice":"0x4a817c800","gas":"0x6691b7","data":"0x608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a723058203b831dd3fa25ac66f76a9d2acca6410eec2722187308c9fdcc7be9459fc21861002900000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009546573744549503230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054549503230000000000000000000000000000000000000000000000000000000","input":"0x608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a723058203b831dd3fa25ac66f76a9d2acca6410eec2722187308c9fdcc7be9459fc21861002900000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009546573744549503230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054549503230000000000000000000000000000000000000000000000000000000","v":"0x1b","s":"0x1ac377eb69f3a3831b11cf9d6db1a236ef9e6285fbedf2a70575391c758157f6","r":"0x198d0cbd90f04d7e7a3f78b3d0b84fb7c795efc336c88f5cc807e5f140835ad0"}}| +2020-01-17 15:46:54.4990|Executing JSON RPC call eth_getblockbynumber with params 0x3,False| +2020-01-17 15:46:54.4990|Sending JSON RPC response: {"id":94,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xfda0f","hash":"0x419519b69e444d4f3fa19adc6aa972ef14987dacad571aabcb38ed2f9bb722df","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x3","parentHash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","receiptsRoot":"0x1ff1e488dc43c0e36366da41bbffe44e3a827bff3e0220eed7b98c69c7b841f5","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x1172","stateRoot":"0xf8a3749ae710fc2941ad7ed531a295f6fc193f76e0085f50134cf70c34290221","step":null,"totalDifficulty":"0x4","timestamp":"0x5e21d6f0","transactions":["0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3"],"transactionsRoot":"0x53c7c142592ab0aa9f729901f7fe57d7a04bc1fef397fda657c69273c928b8cc","uncles":[]}}| +2020-01-17 15:46:54.5154|Executing JSON RPC call eth_getbalance with params 0xb77aec9f59f9d6f39793289a09aea871932619ed,latest| +2020-01-17 15:46:54.5154|Sending JSON RPC response: {"id":95,"jsonrpc":"2.0","result":"0xd3c21b6e4a28b7e95800"}| +2020-01-17 15:46:54.5692|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:54.5692|Executing JSON RPC call eth_getblockbynumber with params latest,True| +2020-01-17 15:46:54.5692|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:54.5692|Sending JSON RPC response: {"id":96,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xfda0f","hash":"0x419519b69e444d4f3fa19adc6aa972ef14987dacad571aabcb38ed2f9bb722df","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x3","parentHash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","receiptsRoot":"0x1ff1e488dc43c0e36366da41bbffe44e3a827bff3e0220eed7b98c69c7b841f5","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x1172","stateRoot":"0xf8a3749ae710fc2941ad7ed531a295f6fc193f76e0085f50134cf70c34290221","step":null,"totalDifficulty":"0x4","timestamp":"0x5e21d6f0","transactions":["0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3"],"transactionsRoot":"0x53c7c142592ab0aa9f729901f7fe57d7a04bc1fef397fda657c69273c928b8cc","uncles":[]}}| +2020-01-17 15:46:54.5692|Sending JSON RPC response: {"id":8728138322692988,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xfda0f","hash":"0x419519b69e444d4f3fa19adc6aa972ef14987dacad571aabcb38ed2f9bb722df","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x3","parentHash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","receiptsRoot":"0x1ff1e488dc43c0e36366da41bbffe44e3a827bff3e0220eed7b98c69c7b841f5","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x1172","stateRoot":"0xf8a3749ae710fc2941ad7ed531a295f6fc193f76e0085f50134cf70c34290221","step":null,"totalDifficulty":"0x4","timestamp":"0x5e21d6f0","transactions":[{"hash":"0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3","nonce":"0x2","blockHash":"0x419519b69e444d4f3fa19adc6aa972ef14987dacad571aabcb38ed2f9bb722df","blockNumber":"0x3","transactionIndex":0,"from":"0xb77aec9f59f9d6f39793289a09aea871932619ed","to":null,"value":"0x0","gasPrice":"0x4a817c800","gas":"0x6691b7","data":"0x608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a723058203b831dd3fa25ac66f76a9d2acca6410eec2722187308c9fdcc7be9459fc21861002900000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009546573744549503230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054549503230000000000000000000000000000000000000000000000000000000","input":"0x608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a723058203b831dd3fa25ac66f76a9d2acca6410eec2722187308c9fdcc7be9459fc21861002900000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009546573744549503230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054549503230000000000000000000000000000000000000000000000000000000","v":"0x1b","s":"0x1ac377eb69f3a3831b11cf9d6db1a236ef9e6285fbedf2a70575391c758157f6","r":"0x198d0cbd90f04d7e7a3f78b3d0b84fb7c795efc336c88f5cc807e5f140835ad0"}],"transactionsRoot":"0x53c7c142592ab0aa9f729901f7fe57d7a04bc1fef397fda657c69273c928b8cc","uncles":[]}}| +2020-01-17 15:46:54.5692|Sending JSON RPC response: {"id":8728138322692987,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xfda0f","hash":"0x419519b69e444d4f3fa19adc6aa972ef14987dacad571aabcb38ed2f9bb722df","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x3","parentHash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","receiptsRoot":"0x1ff1e488dc43c0e36366da41bbffe44e3a827bff3e0220eed7b98c69c7b841f5","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x1172","stateRoot":"0xf8a3749ae710fc2941ad7ed531a295f6fc193f76e0085f50134cf70c34290221","step":null,"totalDifficulty":"0x4","timestamp":"0x5e21d6f0","transactions":[{"hash":"0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3","nonce":"0x2","blockHash":"0x419519b69e444d4f3fa19adc6aa972ef14987dacad571aabcb38ed2f9bb722df","blockNumber":"0x3","transactionIndex":0,"from":"0xb77aec9f59f9d6f39793289a09aea871932619ed","to":null,"value":"0x0","gasPrice":"0x4a817c800","gas":"0x6691b7","data":"0x608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a723058203b831dd3fa25ac66f76a9d2acca6410eec2722187308c9fdcc7be9459fc21861002900000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009546573744549503230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054549503230000000000000000000000000000000000000000000000000000000","input":"0x608060405234801561001057600080fd5b50604051610e30380380610e308339810180604052810190808051906020019092919080518201929190602001805190602001909291908051820192919050505083600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508360008190555082600390805190602001906100b29291906100ee565b5081600460006101000a81548160ff021916908360ff16021790555080600590805190602001906100e49291906100ee565b5050505050610193565b828054600181600116156101000203166002900490600052602060002090601f016020900481019282601f1061012f57805160ff191683800117855561015d565b8280016001018555821561015d579182015b8281111561015c578251825591602001919060010190610141565b5b50905061016a919061016e565b5090565b61019091905b8082111561018c576000816000905550600101610174565b5090565b90565b610c8e806101a26000396000f3006080604052600436106100af576000357c0100000000000000000000000000000000000000000000000000000000900463ffffffff16806306fdde03146100b4578063095ea7b31461014457806318160ddd146101a957806323b872dd146101d457806327e235e314610259578063313ce567146102b05780635c658165146102e157806370a082311461035857806395d89b41146103af578063a9059cbb1461043f578063dd62ed3e146104a4575b600080fd5b3480156100c057600080fd5b506100c961051b565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156101095780820151818401526020810190506100ee565b50505050905090810190601f1680156101365780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561015057600080fd5b5061018f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506105b9565b604051808215151515815260200191505060405180910390f35b3480156101b557600080fd5b506101be6106ab565b6040518082815260200191505060405180910390f35b3480156101e057600080fd5b5061023f600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803590602001909291905050506106b1565b604051808215151515815260200191505060405180910390f35b34801561026557600080fd5b5061029a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061094b565b6040518082815260200191505060405180910390f35b3480156102bc57600080fd5b506102c5610963565b604051808260ff1660ff16815260200191505060405180910390f35b3480156102ed57600080fd5b50610342600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610976565b6040518082815260200191505060405180910390f35b34801561036457600080fd5b50610399600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919050505061099b565b6040518082815260200191505060405180910390f35b3480156103bb57600080fd5b506103c46109e4565b6040518080602001828103825283818151815260200191508051906020019080838360005b838110156104045780820151818401526020810190506103e9565b50505050905090810190601f1680156104315780820380516001836020036101000a031916815260200191505b509250505060405180910390f35b34801561044b57600080fd5b5061048a600480360381019080803573ffffffffffffffffffffffffffffffffffffffff16906020019092919080359060200190929190505050610a82565b604051808215151515815260200191505060405180910390f35b3480156104b057600080fd5b50610505600480360381019080803573ffffffffffffffffffffffffffffffffffffffff169060200190929190803573ffffffffffffffffffffffffffffffffffffffff169060200190929190505050610bdb565b6040518082815260200191505060405180910390f35b60038054600181600116156101000203166002900480601f0160208091040260200160405190810160405280929190818152602001828054600181600116156101000203166002900480156105b15780601f10610586576101008083540402835291602001916105b1565b820191906000526020600020905b81548152906001019060200180831161059457829003601f168201915b505050505081565b600081600260003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167f8c5be1e5ebec7d5bd14f71427d1e84f3dd0314c0f7b2291e5b200ac8c7c3b925846040518082815260200191505060405180910390a36001905092915050565b60005481565b600080600260008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054905082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002054101580156107825750828110155b151561078d57600080fd5b82600160008673ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254019250508190555082600160008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055507fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff8110156108da5782600260008773ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825403925050819055505b8373ffffffffffffffffffffffffffffffffffffffff168573ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef856040518082815260200191505060405180910390a360019150509392505050565b60016020528060005260406000206000915090505481565b600460009054906101000a900460ff1681565b6002602052816000526040600020602052806000526040600020600091509150505481565b6000600160008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050919050565b60058054600181600116156101000203166002900480601f016020809104026020016040519081016040528092919081815260200182805460018160011615610100020316600290048015610a7a5780601f10610a4f57610100808354040283529160200191610a7a565b820191906000526020600020905b815481529060010190602001808311610a5d57829003601f168201915b505050505081565b600081600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000205410151515610ad257600080fd5b81600160003373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff1681526020019081526020016000206000828254039250508190555081600160008573ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020600082825401925050819055508273ffffffffffffffffffffffffffffffffffffffff163373ffffffffffffffffffffffffffffffffffffffff167fddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef846040518082815260200191505060405180910390a36001905092915050565b6000600260008473ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff16815260200190815260200160002060008373ffffffffffffffffffffffffffffffffffffffff1673ffffffffffffffffffffffffffffffffffffffff168152602001908152602001600020549050929150505600a165627a7a723058203b831dd3fa25ac66f76a9d2acca6410eec2722187308c9fdcc7be9459fc21861002900000000000000000000000000000000000000000000000000000000000000640000000000000000000000000000000000000000000000000000000000000080000000000000000000000000000000000000000000000000000000000000001200000000000000000000000000000000000000000000000000000000000000c00000000000000000000000000000000000000000000000000000000000000009546573744549503230000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000054549503230000000000000000000000000000000000000000000000000000000","v":"0x1b","s":"0x1ac377eb69f3a3831b11cf9d6db1a236ef9e6285fbedf2a70575391c758157f6","r":"0x198d0cbd90f04d7e7a3f78b3d0b84fb7c795efc336c88f5cc807e5f140835ad0"}],"transactionsRoot":"0x53c7c142592ab0aa9f729901f7fe57d7a04bc1fef397fda657c69273c928b8cc","uncles":[]}}| +2020-01-17 15:46:54.5827|Executing JSON RPC call eth_getblockbynumber with params latest,False| +2020-01-17 15:46:54.5827|Executing JSON RPC call eth_getblockbynumber with params 0x4,True| +2020-01-17 15:46:54.5827|Executing JSON RPC call eth_getblockbynumber with params 0x4,True| +2020-01-17 15:46:54.5827|Sending JSON RPC response: {"id":8728138322692990,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.5827|Sending JSON RPC response: {"id":8728138322692989,"jsonrpc":"2.0","result":null}| +2020-01-17 15:46:54.5827|Sending JSON RPC response: {"id":97,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xfda0f","hash":"0x419519b69e444d4f3fa19adc6aa972ef14987dacad571aabcb38ed2f9bb722df","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x3","parentHash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","receiptsRoot":"0x1ff1e488dc43c0e36366da41bbffe44e3a827bff3e0220eed7b98c69c7b841f5","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x1172","stateRoot":"0xf8a3749ae710fc2941ad7ed531a295f6fc193f76e0085f50134cf70c34290221","step":null,"totalDifficulty":"0x4","timestamp":"0x5e21d6f0","transactions":["0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3"],"transactionsRoot":"0x53c7c142592ab0aa9f729901f7fe57d7a04bc1fef397fda657c69273c928b8cc","uncles":[]}}| +2020-01-17 15:46:54.6051|Executing JSON RPC call eth_sendrawtransaction with params 0xf889038504a817c800836691b7949bced1be5c820ccb4bb52c1f83862d06b6d02c9f80a4fdacd57600000000000000000000000000000000000000000000000000000000000000021ba0bf106f86a1429252294522082e87f159f52bfe0ca8b756915979518ae5b7c5d1a032a8a5be68083f7b809c342c4c78467a4c78d7275fe1d7abb5b66d9fdf7ec937| +2020-01-17 15:46:54.6051|Broadcasting own transaction 0x19e78ac6c4f317c03d762790fc3f0375ea50ca95de8b3745d2e01b3e2e297624 to 0 peers| +2020-01-17 15:46:54.6051|Sending JSON RPC response: {"id":1579276014599325,"jsonrpc":"2.0","result":"0x19e78ac6c4f317c03d762790fc3f0375ea50ca95de8b3745d2e01b3e2e297624"}| +2020-01-17 15:46:54.6051|Sealed block 4 (0xcf2da6...a678b2), diff: 1, tx count: 1| +2020-01-17 15:46:55.3363|Executing JSON RPC call eth_gettransactionreceipt with params 0x19e78ac6c4f317c03d762790fc3f0375ea50ca95de8b3745d2e01b3e2e297624| +2020-01-17 15:46:55.3363|Sending JSON RPC response: {"id":99,"jsonrpc":"2.0","result":{"transactionHash":"0x19e78ac6c4f317c03d762790fc3f0375ea50ca95de8b3745d2e01b3e2e297624","transactionIndex":"0x0","blockHash":"0xcf2da60d672a67bdc5474fb340b98e6fc13bf8aace4a512e38d75ac650a678b2","blockNumber":"0x4","cumulativeGasUsed":"0x6980","gasUsed":"0x6980","from":"0xb77aec9f59f9d6f39793289a09aea871932619ed","to":"0x9bced1be5c820ccb4bb52c1f83862d06b6d02c9f","contractAddress":null,"logs":[],"logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","root":null,"status":"0x1","error":""}}| +2020-01-17 15:46:56.1313|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:56.1313|Executing JSON RPC call eth_getblockbynumber with params 0x3,False| +2020-01-17 15:46:56.1313|Executing JSON RPC call eth_getblockbynumber with params 0x1,False| +2020-01-17 15:46:56.1313|Executing JSON RPC call eth_getblockbynumber with params 0x2,False| +2020-01-17 15:46:56.1313|Sending JSON RPC response: {"id":100,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0x43b96","hash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x1","parentHash":"0xdb7a61aca921f6660a1a2374fa27a72fed730de06b1081ac3d2648553d5b4d79","receiptsRoot":"0xb4b206e85259edb01f7695b4553249a0a03a376e9a56966ee94ba1ebb74557a3","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x59a","stateRoot":"0x2b765decd3ee1e6e6d70a434897e8dd5144d58059532dc509b53c0f7ab387cd0","step":null,"totalDifficulty":"0x2","timestamp":"0x5e21d6ee","transactions":["0xec3d039f4c30a09e739bc87c924d7265d2848b6077eeae5fa71304ea82c52c9f"],"transactionsRoot":"0xa0509b106a7ba8a42d7be865cd91e806ab94615f2e87dabec2563d3583fa878c","uncles":[]}}| +2020-01-17 15:46:56.1313|Sending JSON RPC response: {"id":101,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xa418","hash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x2","parentHash":"0x8c6f6d14e78a9de36d8497618a4153cdc8deae5b9d4a05ef9b915105f3a2ec48","receiptsRoot":"0x5834bf566ed3a6417b53d443cd8372255e32169894ef8bfde24b52bd5cf5bc8f","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x275","stateRoot":"0xcf25c8fc7fe6303f04ceefa1bb948b86089e0eacf0f4f3e2d2fbc671e263e702","step":null,"totalDifficulty":"0x3","timestamp":"0x5e21d6ef","transactions":["0x6c5e15d74db187bd96091dbc62ca60ef6abd139f8c0c305767ade1fceb76da17"],"transactionsRoot":"0x693b3ea6f9a5410027ff48c3c9d2e0d66199e5d4cafa81fec6828c9434e84121","uncles":[]}}| +2020-01-17 15:46:56.1313|Sending JSON RPC response: {"id":102,"jsonrpc":"2.0","result":{"author":null,"difficulty":"0x1","extraData":"0x4e65746865726d696e64","gasLimit":"0x1000000","gasUsed":"0xfda0f","hash":"0x419519b69e444d4f3fa19adc6aa972ef14987dacad571aabcb38ed2f9bb722df","logsBloom":"0x00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000","miner":"0x0000000000000000000000000000000000000000","mixHash":null,"nonce":"0x0000000000000000","number":"0x3","parentHash":"0x6bd8caf99afb4c16fc63b573a1e32e1d4cd4fd65810fce5dfe07247907f16c3c","receiptsRoot":"0x1ff1e488dc43c0e36366da41bbffe44e3a827bff3e0220eed7b98c69c7b841f5","sha3Uncles":"0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347","signature":null,"size":"0x1172","stateRoot":"0xf8a3749ae710fc2941ad7ed531a295f6fc193f76e0085f50134cf70c34290221","step":null,"totalDifficulty":"0x4","timestamp":"0x5e21d6f0","transactions":["0x53441df3e6bc8b8e4732984102afb6c8a2cf25b1f475a54a497ba2dab0c33fe3"],"transactionsRoot":"0x53c7c142592ab0aa9f729901f7fe57d7a04bc1fef397fda657c69273c928b8cc","uncles":[]}}| +2020-01-17 15:46:57.1463|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:58.1465|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:46:59.1466|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:47:00.1459|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:47:01.1458|Waiting for peers to connect before selecting the sync mode...| +2020-01-17 15:47:02.1556|Waiting for peers to connect before selecting the sync mode...| \ No newline at end of file diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/UnitTests/LedgerSynchroniserTests.cs b/src/Catalyst.Core.Modules.Ledger.Tests/UnitTests/LedgerSynchroniserTests.cs deleted file mode 100644 index b023ec952f..0000000000 --- a/src/Catalyst.Core.Modules.Ledger.Tests/UnitTests/LedgerSynchroniserTests.cs +++ /dev/null @@ -1,155 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading; -using Catalyst.Abstractions.Consensus.Deltas; -using Catalyst.Core.Lib.Util; -using Catalyst.Core.Modules.Hashing; -using Catalyst.Protocol.Deltas; -using Catalyst.TestUtils; -using FluentAssertions; -using LibP2P; -using NSubstitute; -using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using Xunit.Abstractions; - -namespace Catalyst.Core.Modules.Ledger.Tests.UnitTests -{ - public class LedgerSynchroniserTests - { - private readonly ITestOutputHelper _output; - private readonly IDeltaCache _deltaCache; - private readonly LedgerSynchroniser _synchroniser; - private readonly CancellationToken _cancellationToken; - private readonly HashProvider _hashProvider; - - public LedgerSynchroniserTests(ITestOutputHelper output) - { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - - _output = output; - _deltaCache = Substitute.For(); - var logger = Substitute.For(); - - _cancellationToken = new CancellationToken(); - - _synchroniser = new LedgerSynchroniser(_deltaCache, logger); - } - - private Dictionary BuildChainedDeltas(int chainSize) - { - var chainedDeltas = Enumerable.Range(0, chainSize + 1).ToDictionary( - i => CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash(i.ToString())), - i => - { - var previousHash = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash((i - 1).ToString())); - var delta = DeltaHelper.GetDelta(_hashProvider, previousHash); - return delta; - }); - - _output.WriteLine("chain is:"); - _output.WriteLine(string.Join(Environment.NewLine, - chainedDeltas.Select((c, i) => - $"{i}: current {c.Key} | previous {CidHelper.Cast(c.Value.PreviousDeltaDfsHash.ToByteArray())}"))); - return chainedDeltas; - } - - private void SetCacheExpectations(Dictionary deltasByHash) - { - foreach (var delta in deltasByHash) - { - _deltaCache.TryGetOrAddConfirmedDelta(delta.Key, out Arg.Any()) - .Returns(ci => - { - ci[1] = delta.Value; - return true; - }); - } - } - - [Fact] - public void CacheDeltasBetween_Should_Stop_When_One_Of_Deltas_Is_Missing() - { - var chainSize = 5; - var chain = BuildChainedDeltas(chainSize); - SetCacheExpectations(chain); - - var hashes = chain.Keys.ToArray(); - var brokenChainIndex = 2; - _deltaCache.TryGetOrAddConfirmedDelta(hashes[brokenChainIndex], out Arg.Any()) - .Returns(false); - _output.WriteLine($"chain is broken for {hashes[brokenChainIndex]}, it cannot be found on Dfs."); - - var cachedHashes = _synchroniser.CacheDeltasBetween(hashes.First(), - hashes.Last(), _cancellationToken).ToList(); - - OutputCachedHashes(cachedHashes); - - cachedHashes.Count.Should().Be(chainSize - brokenChainIndex); - hashes.TakeLast(chainSize - brokenChainIndex + 1).ToList().ForEach(h => - { - _deltaCache.Received(1).TryGetOrAddConfirmedDelta(h, - out Arg.Any(), _cancellationToken); - }); - } - - private void OutputCachedHashes(List cachedHashes) - { - _output.WriteLine("cached hashes between: "); - _output.WriteLine(string.Join(", ", cachedHashes)); - } - - [Fact] - public void CacheDeltasBetween_Should_Complete_When_LatestKnownDelta_Is_Found() - { - var chainSize = 7; - var chain = BuildChainedDeltas(chainSize); - SetCacheExpectations(chain); - - var hashes = chain.Keys.ToArray(); - - var latestHashIndex = 3; - _output.WriteLine($"Caching deltas between {hashes[latestHashIndex]} and {hashes.Last()}"); - var cachedHashes = _synchroniser.CacheDeltasBetween(hashes[latestHashIndex], - hashes.Last(), _cancellationToken).ToList(); - - var expectedResultLength = chainSize - latestHashIndex + 1; - cachedHashes.Count.Should().Be(expectedResultLength); - - OutputCachedHashes(cachedHashes); - - cachedHashes.Should().BeEquivalentTo(hashes.TakeLast(expectedResultLength)); - - hashes.TakeLast(expectedResultLength - 1).Reverse().ToList().ForEach(h => - { - _deltaCache.Received(1).TryGetOrAddConfirmedDelta(h, - out Arg.Any(), _cancellationToken); - }); - } - } -} diff --git a/src/Catalyst.Core.Modules.Ledger.Tests/UnitTests/LedgerTests.cs b/src/Catalyst.Core.Modules.Ledger.Tests/UnitTests/LedgerTests.cs index ba22a857fa..b27b59a6ea 100644 --- a/src/Catalyst.Core.Modules.Ledger.Tests/UnitTests/LedgerTests.cs +++ b/src/Catalyst.Core.Modules.Ledger.Tests/UnitTests/LedgerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,91 +23,165 @@ using System; using System.Collections.Generic; +using System.Linq; using System.Reactive.Linq; using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.Kvm; using Catalyst.Abstractions.Ledger.Models; using Catalyst.Abstractions.Mempool; +using Catalyst.Abstractions.Sync.Interfaces; using Catalyst.Core.Lib.DAO; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Lib.DAO.Transaction; +using Catalyst.Core.Lib.Extensions.Protocol.Wire; +using Catalyst.Core.Lib.Service; +using Catalyst.Core.Modules.Cryptography.BulletProofs; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Core.Modules.Ledger.Repository; +using Catalyst.Core.Modules.Mempool.Repositories; +using Catalyst.Protocol.Cryptography; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Network; +using Catalyst.Protocol.Transaction; using Catalyst.TestUtils; +using FluentAssertions; +using Google.Protobuf.WellKnownTypes; +using Lib.P2P; using Microsoft.Reactive.Testing; +using MultiFormats.Registry; +using Nethermind.Db; using Nethermind.Dirichlet.Numerics; -using Nethermind.Store; +using Nethermind.State; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using LedgerService = Catalyst.Core.Modules.Ledger.Ledger; +using SharpRepository.InMemoryRepository; +using NUnit.Framework; namespace Catalyst.Core.Modules.Ledger.Tests.UnitTests { + [TestFixture] public sealed class LedgerTests : IDisposable { private readonly TestScheduler _testScheduler; - private LedgerService _ledger; + private Ledger _ledger; private readonly IAccountRepository _fakeRepository; private readonly IDeltaHashProvider _deltaHashProvider; - private readonly IMempool _mempool; + private readonly IMempool _mempool; private readonly ILogger _logger; - private readonly ILedgerSynchroniser _ledgerSynchroniser; + private readonly ISynchroniser _synchroniser; private readonly IHashProvider _hashProvider; - private readonly MultiHash _genesisHash; + private readonly IMapperProvider _mapperProvider; + private readonly Cid _genesisHash; private readonly IDeltaExecutor _executor; private readonly IStateProvider _stateProvider; private readonly IStorageProvider _storageProvider; + private readonly IDeltaByNumberRepository _deltaByNumber; + private readonly ICryptoContext _cryptoContext; + private readonly SigningContext _signingContext; + private readonly IDeltaIndexService _deltaIndexService; + private readonly ITransactionRepository _receipts; public LedgerTests() { _testScheduler = new TestScheduler(); _fakeRepository = Substitute.For(); - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + _mapperProvider = new TestMapperProvider(); _logger = Substitute.For(); - _mempool = Substitute.For>(); + _mempool = Substitute.For>(); _deltaHashProvider = Substitute.For(); - _ledgerSynchroniser = Substitute.For(); - _genesisHash = _hashProvider.ComputeUtf8MultiHash("genesis"); - _ledgerSynchroniser.DeltaCache.GenesisHash.Returns(_genesisHash); + _receipts = Substitute.For(); + _synchroniser = Substitute.For(); + _genesisHash = _hashProvider.ComputeUtf8MultiHash("genesis").ToCid(); + _synchroniser.DeltaCache.GenesisHash.Returns(_genesisHash); _executor = Substitute.For(); _stateProvider = Substitute.For(); _storageProvider = Substitute.For(); - } - - [Fact] - public void Save_Account_State_To_Ledger_Repository() - { - _ledger = new LedgerService(_executor, _stateProvider, _storageProvider, new StateDb(), new StateDb(), _fakeRepository, _deltaHashProvider, _ledgerSynchroniser, _mempool, _logger); - const int numAccounts = 10; - for (var i = 0; i < numAccounts; i++) + _cryptoContext = new FfiWrapper(); + _signingContext = new SigningContext { - var account = AccountHelper.GetAccount((UInt256) i * 5); - _ledger.SaveAccountState(account); - } + NetworkType = NetworkType.Devnet, + SignatureType = SignatureType.TransactionPublic + }; - _fakeRepository.Received(10).Add(Arg.Any()); + _deltaIndexService = new DeltaIndexService(new InMemoryRepository()); } - [Fact] + [Test] public void Should_Reconcile_On_New_Delta_Hash() { - var hash1 = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("update")); - var hash2 = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("update again")); + var hash1 = _hashProvider.ComputeUtf8MultiHash("update").ToCid(); + var hash2 = _hashProvider.ComputeUtf8MultiHash("update again").ToCid(); var updates = new[] {hash1, hash2}; - _ledgerSynchroniser.CacheDeltasBetween(default, default, default) + _synchroniser.CacheDeltasBetween(Arg.Is(_genesisHash), Arg.Is(hash1), default) .ReturnsForAnyArgs(new[] {hash2, hash1, _genesisHash}); _deltaHashProvider.DeltaHashUpdates.Returns(updates.ToObservable(_testScheduler)); - _ledger = new LedgerService(_executor, _stateProvider, _storageProvider, new StateDb(), new StateDb(), _fakeRepository, _deltaHashProvider, _ledgerSynchroniser, _mempool, _logger); + _ledger = new Ledger(_executor, _stateProvider, _storageProvider, new StateDb(), new StateDb(), + _fakeRepository, _deltaIndexService, _receipts, _deltaHashProvider, _synchroniser, _mempool, _mapperProvider, _hashProvider, _logger); + + _testScheduler.Start(); + + _ledger.LatestKnownDelta.Should().Be(_genesisHash); + } + + private IEnumerable GenerateSamplePublicTransactions(int sampleSize) + { + for (var i = 0; i < sampleSize; i++) + { + var sender = _cryptoContext.GeneratePrivateKey(); + var recipient = _cryptoContext.GeneratePrivateKey().GetPublicKey(); + var publicEntry = EntryUtils.PreparePublicEntry(recipient, sender.GetPublicKey(), 10); + publicEntry.Signature = publicEntry.GenerateSignature(_cryptoContext, sender, _signingContext); + yield return publicEntry; + } + } + + [Test] + public void Should_Delete_MempoolItems_On_New_Delta_Hash() + { + var sampleSize = 5; + _mempool.Service.Returns(new MempoolService(new InMemoryRepository())); + + var hash = _hashProvider.ComputeUtf8MultiHash("update").ToCid(); + var updates = new[] {hash}; + + _synchroniser.CacheDeltasBetween(Arg.Is(_genesisHash), Arg.Is(hash), default) + .ReturnsForAnyArgs(new[] {hash, _genesisHash}); + + var allPublicEntries = GenerateSamplePublicTransactions(sampleSize * 2).ToList(); + + //Add all public entries to the mempool + allPublicEntries.Select(x => x.ToDao(_mapperProvider)).ToList() + .ForEach(x => _mempool.Service.CreateItem(x)); + + //Only add half of all public entries to the delta + var delta = new Delta + { + TimeStamp = Timestamp.FromDateTime(DateTime.UtcNow), + PublicEntries = {allPublicEntries.Take(sampleSize)} + }; + + _synchroniser.DeltaCache.TryGetOrAddConfirmedDelta(Arg.Is(hash), out Arg.Any()).Returns(x => + { + x[1] = delta; + return true; + }); + + _deltaHashProvider.DeltaHashUpdates.Returns(updates.ToObservable(_testScheduler)); + + _ledger = new Ledger(_executor, _stateProvider, _storageProvider, new StateDb(), new StateDb(), + _fakeRepository, _deltaIndexService, _receipts, _deltaHashProvider, _synchroniser, _mempool, _mapperProvider, _hashProvider, _logger); _testScheduler.Start(); - _mempool.Repository.ReceivedWithAnyArgs(updates.Length).Delete(Arg.Any>()); + _mempool.Service.GetAll().Should().HaveCount(sampleSize); } public void Dispose() diff --git a/src/Catalyst.Core.Modules.Ledger/Catalyst.Core.Modules.Ledger.csproj b/src/Catalyst.Core.Modules.Ledger/Catalyst.Core.Modules.Ledger.csproj index ec4b9f2403..706c565559 100644 --- a/src/Catalyst.Core.Modules.Ledger/Catalyst.Core.Modules.Ledger.csproj +++ b/src/Catalyst.Core.Modules.Ledger/Catalyst.Core.Modules.Ledger.csproj @@ -1,19 +1,25 @@  - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Ledger - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Ledger.snk true + + 1701;1702;CS8002 + + + + diff --git a/src/Catalyst.Core.Modules.Ledger/Contract/CallableContractProxy.cs b/src/Catalyst.Core.Modules.Ledger/Contract/CallableContractProxy.cs new file mode 100644 index 0000000000..38e45bc779 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger/Contract/CallableContractProxy.cs @@ -0,0 +1,105 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Contract; +using Catalyst.Abstractions.Kvm; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Transaction; +using Google.Protobuf; +using Lib.P2P; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Dirichlet.Numerics; +using Nethermind.Evm.Tracing; +using Nethermind.State; + +namespace Catalyst.Core.Modules.Ledger.Contract +{ + public class CallableContractProxy : ICallableContractProxy + { + private readonly IDeltaHashProvider _deltaHashProvider; + private readonly IDeltaCache _deltaCache; + private readonly IDeltaExecutor _deltaExecutor; + private readonly IStateProvider _stateProvider; + private readonly IStorageProvider _storageProvider; + + private static ulong DefaultContractGasLimit = 1_600_000L; + + public CallableContractProxy(IDeltaHashProvider deltaHashProvider, IDeltaCache deltaCache, IDeltaExecutor deltaExecutor, IStateProvider stateProvider, IStorageProvider storageProvider) + { + _deltaHashProvider = deltaHashProvider; + _deltaCache = deltaCache; + _deltaExecutor = deltaExecutor; + _stateProvider = stateProvider; + _storageProvider = storageProvider; + } + + public Delta CreateOneOffDelta(Cid cid, Delta delta, PublicEntry publicEntry) + { + Delta newDelta = delta.Clone(); + newDelta.PreviousDeltaDfsHash = cid.ToArray().ToByteString(); + newDelta.CoinbaseEntries.Clear(); + newDelta.ConfidentialEntries.Clear(); + newDelta.PublicEntries.Clear(); + newDelta.PublicEntries.Add(publicEntry); + return newDelta; + } + + private PublicEntry GenerateTransaction(Address contractAddress, byte[] transactionData) + { + return new PublicEntry + { + Nonce = 0, + SenderAddress = Address.SystemUser.Bytes.ToByteString(), + ReceiverAddress = contractAddress?.Bytes.ToByteString(), + GasLimit = DefaultContractGasLimit, + GasPrice = UInt256.Zero.ToUint256ByteString(), + Amount = UInt256.Zero.ToUint256ByteString(), + Data = transactionData?.ToByteString() ?? ByteString.Empty + }; + } + + public byte[] Call(Address contractAddress, byte[] data) + { + var transaction = GenerateTransaction(contractAddress, data); + + var latestDeltaCid = _deltaHashProvider.GetLatestDeltaHash(); + _deltaCache.TryGetOrAddConfirmedDelta(latestDeltaCid, out Delta latestDelta); + + Keccak root = latestDelta.StateRoot.ToKeccak(); + + var newDelta = CreateOneOffDelta(latestDeltaCid, latestDelta, transaction); + + CallOutputTracer callOutputTracer = new(); + + _stateProvider.StateRoot = root; + _deltaExecutor.CallAndReset(newDelta, callOutputTracer); + _stateProvider.Reset(); + _storageProvider.Reset(); + + return callOutputTracer.ReturnValue; + } + } +} diff --git a/src/Catalyst.Core.Modules.Ledger/DeltaResolver.cs b/src/Catalyst.Core.Modules.Ledger/DeltaResolver.cs index 0033a83495..7e06847075 100644 --- a/src/Catalyst.Core.Modules.Ledger/DeltaResolver.cs +++ b/src/Catalyst.Core.Modules.Ledger/DeltaResolver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,20 +22,27 @@ #endregion using Catalyst.Abstractions.Kvm; -using Catalyst.Protocol.Deltas; -using Nethermind.Core.Crypto; +using Catalyst.Abstractions.Ledger; +using Catalyst.Core.Lib.Service; +using Lib.P2P; namespace Catalyst.Core.Modules.Ledger { - public class DeltaResolver : IDeltaResolver + public sealed class DeltaResolver : IDeltaResolver { - public Keccak Resolve(int deltaNumber) + readonly IDeltaIndexService _deltaIndexService; + readonly ILedger _ledger; + + public DeltaResolver(IDeltaIndexService deltaIndexService, ILedger ledger) { - throw new System.NotImplementedException(); + _deltaIndexService = deltaIndexService; + _ledger = ledger; } - public Delta Latest { get; } - public Delta Earliest { get; } - public Delta Pending { get; } + public bool TryResolve(long deltaNumber, out Cid deltaHash) => _deltaIndexService.TryFind(deltaNumber, out deltaHash); + + public long LatestDeltaNumber => _ledger.LatestKnownDeltaNumber; + + public Cid LatestDelta => _ledger.LatestKnownDelta; } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Ledger/Ledger.cs b/src/Catalyst.Core.Modules.Ledger/Ledger.cs index b46cf8aca5..de815f7f1d 100644 --- a/src/Catalyst.Core.Modules.Ledger/Ledger.cs +++ b/src/Catalyst.Core.Modules.Ledger/Ledger.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,20 +25,27 @@ using System.Linq; using System.Threading; using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.Kvm; using Catalyst.Abstractions.Ledger; +using Catalyst.Abstractions.Ledger.Models; using Catalyst.Abstractions.Mempool; +using Catalyst.Abstractions.Sync.Interfaces; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Lib.DAO.Transaction; +using Catalyst.Core.Lib.Service; using Catalyst.Core.Modules.Ledger.Repository; using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Transaction; using Dawn; -using LibP2P; +using Google.Protobuf; +using Lib.P2P; using Nethermind.Core.Crypto; -using Nethermind.Evm.Tracing; -using Nethermind.Store; +using Nethermind.Db; +using Nethermind.State; using Serilog; using Serilog.Events; -using Account = Catalyst.Abstractions.Ledger.Models.Account; namespace Catalyst.Core.Modules.Ledger { @@ -55,14 +62,25 @@ public sealed class Ledger : ILedger, IDisposable private readonly IStorageProvider _storageProvider; private readonly ISnapshotableDb _stateDb; private readonly ISnapshotableDb _codeDb; - private readonly ILedgerSynchroniser _synchroniser; - private readonly IMempool _mempool; + private readonly ITransactionRepository _receipts; + private readonly IMempool _mempool; + private readonly IMapperProvider _mapperProvider; + private readonly IHashProvider _hashProvider; private readonly ILogger _logger; private readonly IDisposable _deltaUpdatesSubscription; - private readonly object _synchronisationLock = new object(); + private readonly object _synchronisationLock = new(); + volatile Cid _latestKnownDelta; + long _latestKnownDeltaNumber = -1; + + public Cid LatestKnownDelta => _latestKnownDelta; + + public long LatestKnownDeltaNumber => Volatile.Read(ref _latestKnownDeltaNumber); + + private readonly IDeltaIndexService _deltaIndexService; + + private readonly ISynchroniser _synchroniser; - public Cid LatestKnownDelta { get; private set; } public bool IsSynchonising => Monitor.IsEntered(_synchronisationLock); public Ledger(IDeltaExecutor deltaExecutor, @@ -71,29 +89,55 @@ public Ledger(IDeltaExecutor deltaExecutor, ISnapshotableDb stateDb, ISnapshotableDb codeDb, IAccountRepository accounts, + IDeltaIndexService deltaIndexService, + ITransactionRepository receipts, IDeltaHashProvider deltaHashProvider, - ILedgerSynchroniser synchroniser, - IMempool mempool, + ISynchroniser synchroniser, + IMempool mempool, + IMapperProvider mapperProvider, + IHashProvider hashProvider, ILogger logger) { Accounts = accounts; _deltaExecutor = deltaExecutor; _stateProvider = stateProvider; _storageProvider = storageProvider; + _stateDb = stateDb; _codeDb = codeDb; - _synchroniser = synchroniser; _mempool = mempool; + _mapperProvider = mapperProvider; + _hashProvider = hashProvider; _logger = logger; + _receipts = receipts; + _synchroniser = synchroniser; _deltaUpdatesSubscription = deltaHashProvider.DeltaHashUpdates.Subscribe(Update); - LatestKnownDelta = _synchroniser.DeltaCache.GenesisHash; + + _deltaIndexService = deltaIndexService; + + var latestDeltaIndex = _deltaIndexService.LatestDeltaIndex(); + if (latestDeltaIndex != null) + { + _latestKnownDelta = latestDeltaIndex.Cid; + _latestKnownDeltaNumber = (long)latestDeltaIndex.Height; + return; + } + + _latestKnownDelta = _synchroniser.DeltaCache.GenesisHash; + WriteLatestKnownDelta(_latestKnownDelta); } - private void FlushTransactionsFromDelta() + private void FlushTransactionsFromDelta(Cid deltaHash) { - var transactionsToFlush = _mempool.Repository.GetAll(); //@TOD0 no get alls - _mempool.Repository.Delete(transactionsToFlush); + _synchroniser.DeltaCache.TryGetOrAddConfirmedDelta(deltaHash, out var delta); + if (delta != null) + { + var deltaTransactions = + delta.PublicEntries.Select(x => x.ToDao(_mapperProvider)); + + _mempool.Service.Delete(deltaTransactions); + } } /// @@ -121,7 +165,7 @@ public void Update(Cid deltaHash) lock (_synchronisationLock) { var chainedDeltaHashes = _synchroniser - .CacheDeltasBetween(LatestKnownDelta, deltaHash, CancellationToken.None) + .CacheDeltasBetween(LatestKnownDelta, deltaHash, CancellationToken.None)? .Reverse() .ToList(); @@ -133,95 +177,99 @@ public void Update(Cid deltaHash) return; } - foreach (var chainedDeltaHash in chainedDeltaHashes) + foreach (var chainedDeltaHash in chainedDeltaHashes.Skip(1)) { UpdateLedgerFromDelta(chainedDeltaHash); } } - //https://github.com/catalyst-network/Catalyst.Node/issues/871 - FlushTransactionsFromDelta(); + FlushTransactionsFromDelta(deltaHash); } catch (Exception exception) { _logger.Error(exception, "Failed to update the ledger using the delta with hash {deltaHash}", deltaHash); + Environment.Exit(2); } } private void UpdateLedgerFromDelta(Cid deltaHash) { var stateSnapshot = _stateDb.TakeSnapshot(); - var codeSnapshot = _codeDb.TakeSnapshot(); - if (stateSnapshot != -1 || codeSnapshot != -1) + if (stateSnapshot != -1) { if (_logger.IsEnabled(LogEventLevel.Error)) { - _logger.Error("Uncommitted state ({stateSnapshot}, {codeSnapshot}) when processing from a branch root {branchStateRoot} starting with delta {deltaHash}", + _logger.Error("Uncommitted state ({stateSnapshot}) when processing from a branch root {branchStateRoot} starting with delta {deltaHash}", stateSnapshot, - codeSnapshot, null, deltaHash); } } - + var snapshotStateRoot = _stateProvider.StateRoot; - // this code should be brought in / used as a reference if reorganization behaviour is known - //// if (branchStateRoot != null && _stateProvider.StateRoot != branchStateRoot) - //// { - //// /* discarding the other branch data - chain reorganization */ - //// Metrics.Reorganizations++; - //// _storageProvider.Reset(); - //// _stateProvider.Reset(); - //// _stateProvider.StateRoot = branchStateRoot; - //// } - try { - if (!_synchroniser.DeltaCache.TryGetOrAddConfirmedDelta(deltaHash, out Delta nextDeltaInChain)) + if (!_synchroniser.DeltaCache.TryGetOrAddConfirmedDelta(deltaHash, out var nextDeltaInChain)) { - _logger.Warning( - "Failed to retrieve Delta with hash {hash} from the Dfs, ledger has not been updated.", - deltaHash); + _logger.Warning("Failed to retrieve Delta with hash {hash} from the Dfs, ledger has not been updated.", deltaHash); return; } + Cid parentCid = Cid.Read(nextDeltaInChain.PreviousDeltaDfsHash.ToByteArray()); + if (!_synchroniser.DeltaCache.TryGetOrAddConfirmedDelta(parentCid, out Delta parentDelta)) + { + _logger.Warning("Failed to retrieve parent Delta with hash {hash} from the Dfs, ledger has not been updated.", deltaHash); + return; + } + + ReceiptDeltaTracer tracer = new(nextDeltaInChain, deltaHash); + // add here a receipts tracer or similar, depending on what data needs to be stored for each contract - _deltaExecutor.Execute(nextDeltaInChain, NullTxTracer.Instance); - - // this code should be brought in / used as a reference if reorganization behaviour is known - //// delta testing here (for delta production) - //// if ((options & ProcessingOptions.ReadOnlyChain) != 0) - //// { - //// Restore(stateSnapshot, codeSnapshot, snapshotStateRoot); - //// } - //// else - //// { - //// _stateDb.Commit(); - //// _codeDb.Commit(); - //// } - + + _stateProvider.Reset(); + _storageProvider.Reset(); + + _stateProvider.StateRoot = new Keccak(parentDelta.StateRoot?.ToByteArray()); + _deltaExecutor.Execute(nextDeltaInChain, tracer); + + // store receipts + if (tracer.Receipts.Any()) + { + _receipts.Put(deltaHash, tracer.Receipts.ToArray(), nextDeltaInChain.PublicEntries.ToArray()); + } + _stateDb.Commit(); _codeDb.Commit(); - LatestKnownDelta = deltaHash; + _latestKnownDelta = deltaHash; + + WriteLatestKnownDelta(deltaHash); } catch { - Restore(stateSnapshot, codeSnapshot, snapshotStateRoot); + Restore(stateSnapshot, snapshotStateRoot); } } - - private void Restore(int stateSnapshot, int codeSnapshot, Keccak snapshotStateRoot) + + void WriteLatestKnownDelta(Cid deltaHash) + { + _latestKnownDelta = deltaHash; + + Volatile.Write(ref _latestKnownDeltaNumber, _latestKnownDeltaNumber + 1); + _deltaIndexService.Map(_latestKnownDeltaNumber, deltaHash); // store delta numbers + _synchroniser.UpdateState((ulong)_latestKnownDeltaNumber); + } + + private void Restore(int stateSnapshot, Keccak snapshotStateRoot) { if (_logger.IsEnabled(LogEventLevel.Verbose)) { _logger.Verbose("Reverting deltas {stateRoot}", _stateProvider.StateRoot); } - + _stateDb.Restore(stateSnapshot); - _codeDb.Restore(codeSnapshot); _storageProvider.Reset(); _stateProvider.Reset(); _stateProvider.StateRoot = snapshotStateRoot; diff --git a/src/Catalyst.Core.Modules.Ledger/LedgerModule.cs b/src/Catalyst.Core.Modules.Ledger/LedgerModule.cs index 13447db9e1..553feb8a42 100644 --- a/src/Catalyst.Core.Modules.Ledger/LedgerModule.cs +++ b/src/Catalyst.Core.Modules.Ledger/LedgerModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,26 +25,42 @@ using Catalyst.Abstractions.Kvm; using Catalyst.Abstractions.Ledger; using Catalyst.Abstractions.Ledger.Models; +using Catalyst.Core.Modules.Kvm; using Catalyst.Core.Modules.Ledger.Repository; -using SharpRepository.InMemoryRepository; using SharpRepository.Repository; +using SharpRepository.MongoDbRepository; +using Catalyst.Core.Modules.Ledger.Contract; +using Catalyst.Abstractions.Contract; namespace Catalyst.Core.Modules.Ledger { - public class LedgerModule : Module + public class LedgerModule : Autofac.Module { protected override void Load(ContainerBuilder builder) { - builder.Register(c => new InMemoryRepository()) + builder.Register(c => new MongoDbRepository()) .As>() .SingleInstance(); - builder.RegisterType().As(); + builder.Register(c => new MongoDbRepository()) + .As>() + .SingleInstance(); + + builder.Register(c => new MongoDbRepository()) + .As>() + .SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As().SingleInstance(); + + builder.RegisterType().As().SingleInstance() + .WithExecutionParameters(builder) + .WithStateDbParameters(builder); + + builder.RegisterType().As().SingleInstance().WithExecutionParameters(builder); + + builder.RegisterType().As().SingleInstance().WithExecutionParameters(builder); } } } diff --git a/src/Catalyst.Core.Modules.Ledger/LedgerSynchroniser.cs b/src/Catalyst.Core.Modules.Ledger/LedgerSynchroniser.cs deleted file mode 100644 index 5c033435fb..0000000000 --- a/src/Catalyst.Core.Modules.Ledger/LedgerSynchroniser.cs +++ /dev/null @@ -1,75 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System.Collections.Generic; -using System.Threading; -using Catalyst.Abstractions.Consensus.Deltas; -using Catalyst.Core.Lib.Util; -using LibP2P; -using Serilog; - -namespace Catalyst.Core.Modules.Ledger -{ - /// - public class LedgerSynchroniser : ILedgerSynchroniser - { - private readonly ILogger _logger; - - public LedgerSynchroniser(IDeltaCache deltaCache, ILogger logger) - { - DeltaCache = deltaCache; - _logger = logger; - } - - /// - public IDeltaCache DeltaCache { get; } - - /// - public IEnumerable CacheDeltasBetween(Cid latestKnownDeltaHash, - Cid targetDeltaHash, - CancellationToken cancellationToken) - { - var thisHash = targetDeltaHash; - - do - { - if (!DeltaCache.TryGetOrAddConfirmedDelta(thisHash, out var retrievedDelta, cancellationToken)) - { - yield break; - } - - var previousDfsHash = CidHelper.Cast(retrievedDelta.PreviousDeltaDfsHash.ToByteArray()); - - _logger.Debug("Retrieved delta {previous} as predecessor of {current}", - previousDfsHash, thisHash); - - yield return thisHash; - - thisHash = previousDfsHash; - } while (!thisHash.Equals(latestKnownDeltaHash) - && !cancellationToken.IsCancellationRequested); - - yield return thisHash; - } - } -} diff --git a/src/Catalyst.Core.Modules.Ledger/LinkedDelta.cs b/src/Catalyst.Core.Modules.Ledger/LinkedDelta.cs index 24f0be4599..01059bd1ec 100644 --- a/src/Catalyst.Core.Modules.Ledger/LinkedDelta.cs +++ b/src/Catalyst.Core.Modules.Ledger/LinkedDelta.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Ledger/PoaDeltaProcessor.cs b/src/Catalyst.Core.Modules.Ledger/PoaDeltaProcessor.cs new file mode 100644 index 0000000000..4f86703431 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger/PoaDeltaProcessor.cs @@ -0,0 +1,27 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.Core.Modules.Ledger +{ + public class PoaDeltaProcessor { } +} diff --git a/src/Catalyst.Core.Modules.Ledger/ReceiptDeltaTracer.cs b/src/Catalyst.Core.Modules.Ledger/ReceiptDeltaTracer.cs new file mode 100644 index 0000000000..48cabd2c53 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger/ReceiptDeltaTracer.cs @@ -0,0 +1,146 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using Catalyst.Abstractions.Ledger.Models; +using Catalyst.Core.Modules.Kvm; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Transaction; +using Google.Protobuf; +using Lib.P2P; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Dirichlet.Numerics; +using Nethermind.Evm; +using Nethermind.Evm.Tracing; +using Nethermind.State; + +namespace Catalyst.Core.Modules.Ledger +{ + public sealed class ReceiptDeltaTracer : ITxTracer + { + readonly Delta _delta; + readonly long _deltaNumber; + readonly Cid _deltaHash; + readonly List _txReceipts; + int _currentIndex; + + public ReceiptDeltaTracer(Delta delta, Cid deltaHash) + { + _delta = delta; + _deltaHash = deltaHash; + _deltaNumber = delta.DeltaNumber; + _txReceipts = new List(delta.PublicEntries.Count); + } + + public IEnumerable Receipts => _txReceipts; + + public bool IsTracingReceipt => true; + + public bool IsTracingBlockHash => false; + public void MarkAsSuccess(Address recipient, long gasSpent, byte[] output, LogEntry[] logs, Keccak _) { _txReceipts.Add(BuildReceipt(recipient, gasSpent, StatusCode.Success, logs)); } + + public void MarkAsFailed(Address recipient, long gasSpent, byte[] output, string error, Keccak _) { _txReceipts.Add(BuildFailedReceipt(recipient, gasSpent)); } + + private TransactionReceipt BuildFailedReceipt(Address recipient, long gasSpent) { return BuildReceipt(recipient, gasSpent, StatusCode.Failure, LogEntry.EmptyLogs); } + + private TransactionReceipt BuildReceipt(Address recipient, long spentGas, byte statusCode, LogEntry[] logEntries) + { + PublicEntry entry = _delta.PublicEntries[_currentIndex]; + + TransactionReceipt txReceipt = new TransactionReceipt + { + Logs = logEntries, + GasUsedTotal = _delta.GasUsed, + StatusCode = statusCode, + Recipient = entry.IsContractDeployment ? null : recipient.ToString(), + DeltaHash = _deltaHash, + DeltaNumber = _deltaNumber, + Index = _currentIndex, + GasUsed = spentGas, + Sender = GetAccountAddress(entry.SenderAddress).ToString(), + ContractAddress = entry.IsContractDeployment ? recipient.ToString() : null, + }; + + _currentIndex += 1; + + return txReceipt; + } + + private static Address GetAccountAddress(ByteString publicKeyByteString) + { + if (publicKeyByteString == null || publicKeyByteString.IsEmpty) + { + return null; + } + + return publicKeyByteString.ToByteArray().ToKvmAddress(); + } + + public bool IsTracingActions => false; + public bool IsTracingOpLevelStorage => false; + public bool IsTracingMemory => false; + public bool IsTracingInstructions => false; + public bool IsTracingRefunds => false; + public bool IsTracingCode => false; + public bool IsTracingStack => false; + public bool IsTracingState => false; + public void ReportBalanceChange(Address address, UInt256? before, UInt256? after) { throw new NotImplementedException(); } + public void ReportCodeChange(Address address, byte[] before, byte[] after) { throw new NotImplementedException(); } + public void ReportNonceChange(Address address, UInt256? before, UInt256? after) { throw new NotImplementedException(); } + public void ReportAccountRead(Address address) { throw new NotImplementedException(); } + public void ReportStorageChange(StorageCell storageAddress, byte[] before, byte[] after) { throw new NotImplementedException(); } + public void StartOperation(int depth, long gas, Instruction opcode, int pc) { throw new NotImplementedException(); } + public void ReportOperationError(EvmExceptionType error) { throw new NotImplementedException(); } + public void ReportOperationRemainingGas(long gas) { throw new NotImplementedException(); } + public void SetOperationStack(List stackTrace) { throw new NotImplementedException(); } + public void ReportStackPush(Span stackItem) { throw new NotImplementedException(); } + public void SetOperationMemory(List memoryTrace) { throw new NotImplementedException(); } + public void SetOperationMemorySize(ulong newSize) { throw new NotImplementedException(); } + public void ReportMemoryChange(long offset, Span data) { throw new NotImplementedException(); } + public void ReportStorageChange(Span key, Span value) { throw new NotImplementedException(); } + public void SetOperationStorage(Address address, UInt256 storageIndex, byte[] newValue, byte[] currentValue) { throw new NotImplementedException(); } + public void ReportSelfDestruct(Address address, UInt256 balance, Address refundAddress) { throw new NotImplementedException(); } + + public void ReportAction(long gas, + UInt256 value, + Address @from, + Address to, + byte[] input, + ExecutionType callType, + bool isPrecompileCall = false) + { + throw new NotImplementedException(); + } + + public void ReportActionEnd(long gas, byte[] output) { throw new NotImplementedException(); } + public void ReportActionError(EvmExceptionType evmExceptionType) { throw new NotImplementedException(); } + public void ReportActionEnd(long gas, Address deploymentAddress, byte[] deployedCode) { throw new NotImplementedException(); } + public void ReportBlockHash(Keccak blockHash) { throw new NotImplementedException(); } + public void ReportByteCode(byte[] byteCode) { throw new NotImplementedException(); } + public void ReportGasUpdateForVmTrace(long refund, long gasAvailable) { throw new NotImplementedException(); } + public void ReportRefund(long gasAvailable) { throw new NotImplementedException(); } + public void ReportExtraGasPressure(long extraGasPressure) { throw new NotImplementedException(); } + } +} diff --git a/src/Catalyst.Core.Modules.Ledger/Repository/AccountRepository.cs b/src/Catalyst.Core.Modules.Ledger/Repository/AccountRepository.cs index 913eaea552..125963da2c 100644 --- a/src/Catalyst.Core.Modules.Ledger/Repository/AccountRepository.cs +++ b/src/Catalyst.Core.Modules.Ledger/Repository/AccountRepository.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,13 +22,27 @@ #endregion using Catalyst.Abstractions.Ledger.Models; -using Catalyst.Core.Lib.Repository; using SharpRepository.Repository; namespace Catalyst.Core.Modules.Ledger.Repository { - public class AccountRepository : RepositoryWrapper, IAccountRepository + public class AccountRepository : IAccountRepository { - public AccountRepository(IRepository repository) : base(repository) { } + private readonly IRepository _repository; + + public AccountRepository(IRepository repository) + { + _repository = repository; + } + + public void Add(Account account) + { + _repository.Add(account); + } + + public void Dispose() + { + _repository.Dispose(); + } } } diff --git a/src/Catalyst.Core.Modules.Ledger/Repository/DeltaByNumberRepository.cs b/src/Catalyst.Core.Modules.Ledger/Repository/DeltaByNumberRepository.cs new file mode 100644 index 0000000000..2b140d4a34 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger/Repository/DeltaByNumberRepository.cs @@ -0,0 +1,59 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Ledger.Models; +using Lib.P2P; +using SharpRepository.Repository; + +namespace Catalyst.Core.Modules.Ledger.Repository +{ + public class DeltaByNumberRepository : IDeltaByNumberRepository + { + readonly IRepository _repository; + + public DeltaByNumberRepository(IRepository repository) + { + _repository = repository; + } + + public void Map(long deltaNumber, Cid deltaHash) + { + if (!_repository.TryGet(DeltaByNumber.BuildDocumentId(deltaNumber), out _)) + { + _repository.Add(new DeltaByNumber(deltaNumber, deltaHash)); + } + } + + public bool TryFind(long deltaNumber, out Cid deltaHash) + { + if (_repository.TryGet(DeltaByNumber.BuildDocumentId(deltaNumber), out var delta)) + { + deltaHash = delta.DeltaId; + return true; + } + + deltaHash = default; + return false; + } + } +} diff --git a/src/Catalyst.Core.Modules.Ledger/Repository/IAccountRepository.cs b/src/Catalyst.Core.Modules.Ledger/Repository/IAccountRepository.cs index f26bfcae29..bd5fc58e2c 100644 --- a/src/Catalyst.Core.Modules.Ledger/Repository/IAccountRepository.cs +++ b/src/Catalyst.Core.Modules.Ledger/Repository/IAccountRepository.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,13 @@ #endregion +using System; using Catalyst.Abstractions.Ledger.Models; -using Catalyst.Abstractions.Repository; namespace Catalyst.Core.Modules.Ledger.Repository { - public interface IAccountRepository : IRepositoryWrapper { } + public interface IAccountRepository : IDisposable + { + void Add(Account account); + } } diff --git a/src/Catalyst.Core.Modules.Ledger/Repository/IDeltaByNumberRepository.cs b/src/Catalyst.Core.Modules.Ledger/Repository/IDeltaByNumberRepository.cs new file mode 100644 index 0000000000..9e2fbaded7 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger/Repository/IDeltaByNumberRepository.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Lib.P2P; + +namespace Catalyst.Core.Modules.Ledger.Repository +{ + public interface IDeltaByNumberRepository + { + /// + /// Tries to find the mapping between the and the . + /// + /// Whether the mapping was found. + bool TryFind(long deltaNumber, out Cid deltaHash); + + /// + /// Maps the to . + /// + /// + /// + void Map(long deltaNumber, Cid deltaHash); + } +} diff --git a/src/Catalyst.Core.Modules.Ledger/Repository/ITransactionRepository.cs b/src/Catalyst.Core.Modules.Ledger/Repository/ITransactionRepository.cs new file mode 100644 index 0000000000..360da00642 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger/Repository/ITransactionRepository.cs @@ -0,0 +1,39 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Ledger.Models; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Transaction; +using Lib.P2P; +using Nethermind.Core.Crypto; + +namespace Catalyst.Core.Modules.Ledger.Repository +{ + public interface ITransactionRepository + { + void Put(Cid deltaHash, TransactionReceipt[] receipts, PublicEntry[] deltaPublicEntries); + bool TryFind(Cid deltaHash, out TransactionReceipt[] receipts); + bool TryFind(Keccak transactionHash, out TransactionReceipt receipts); + bool TryFind(Keccak transactionHash, out Cid deltaHash, out Delta delta, out int index); + } +} diff --git a/src/Catalyst.Core.Modules.Ledger/Repository/TransactionRepository.cs b/src/Catalyst.Core.Modules.Ledger/Repository/TransactionRepository.cs new file mode 100644 index 0000000000..b903ff64f2 --- /dev/null +++ b/src/Catalyst.Core.Modules.Ledger/Repository/TransactionRepository.cs @@ -0,0 +1,169 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Ledger.Models; +using Catalyst.Abstractions.Repository; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Transaction; +using Lib.P2P; +using Nethermind.Core.Crypto; +using SharpRepository.Repository; + +namespace Catalyst.Core.Modules.Ledger.Repository +{ + public class TransactionRepository : ITransactionRepository + { + readonly IRepository _repository; + readonly IRepository _transactionToDeltaRepository; + readonly IHashProvider _hashProvider; + readonly IDeltaCache _deltaCache; + + public TransactionRepository(IRepository repository, + IRepository transactionToDeltaRepository, + IHashProvider hashProvider, + IDeltaCache deltaCache) + { + _repository = repository; + _transactionToDeltaRepository = transactionToDeltaRepository; + _hashProvider = hashProvider; + _deltaCache = deltaCache; + } + + public void Put(Cid deltaHash, TransactionReceipt[] receipts, PublicEntry[] deltaPublicEntries) + { + if (!_repository.TryGet(GetDocumentId(deltaHash), out _)) + { + _repository.Add(new TransactionReceipts + { + Id = GetDocumentId(deltaHash), + Receipts = receipts + }); + + for (var i = 0; i < receipts.Length; i++) + { + var transactionHash = deltaPublicEntries[i].GetDocumentId(_hashProvider); + if (_transactionToDeltaRepository.TryGet(transactionHash, out _)) + { + _transactionToDeltaRepository.Delete(transactionHash); + } + + _transactionToDeltaRepository.Add(new TransactionToDelta {DeltaHash = deltaHash, Id = transactionHash}); + } + } + } + + public bool TryFind(Cid deltaHash, out TransactionReceipt[] receipts) + { + if (_repository.TryGet(GetDocumentId(deltaHash), out var existing)) + { + receipts = existing.Receipts; + return true; + } + + receipts = default; + return false; + } + + public bool TryFind(Keccak transactionHash, out TransactionReceipt receipt) + { + var id = transactionHash.AsDocumentId(); + + if (!TryFetchData(id, out var deltaHash, out var delta)) + { + receipt = default; + return false; + } + + var index = 0; + + foreach (var publicEntry in delta.PublicEntries) + { + var documentId = publicEntry.GetDocumentId(_hashProvider); + if (id == documentId) + { + var key = GetDocumentId(deltaHash); + if (_repository.TryGet(key, out var receipts)) + { + receipt = receipts.Receipts[index]; + return true; + } + + receipt = default; + return false; + } + + index++; + } + + receipt = default; + return false; + } + + public bool TryFind(Keccak transactionHash, out Cid deltaHash, out Delta delta, out int index) + { + var id = transactionHash.AsDocumentId(); + + if (!TryFetchData(id, out deltaHash, out delta)) + { + index = 0; + return false; + } + + index = 0; + foreach (var pe in delta.PublicEntries) + { + var documentId = pe.GetDocumentId(_hashProvider); + if (id == documentId) + { + return true; + } + + index++; + } + + return false; + } + + bool TryFetchData(string transactionId, out Cid deltaHash, out Delta delta) + { + if (!_transactionToDeltaRepository.TryGet(transactionId, out var transactionToDelta)) + { + deltaHash = null; + delta = null; + return false; + } + + deltaHash = transactionToDelta.DeltaHash; + if (!_deltaCache.TryGetOrAddConfirmedDelta(deltaHash, out delta)) + { + return false; + } + + return true; + } + + static string GetDocumentId(Cid deltaHash) => deltaHash.ToString(); + } +} diff --git a/src/Catalyst.Core.Modules.Ledger/Web3EthApi.cs b/src/Catalyst.Core.Modules.Ledger/Web3EthApi.cs index b8e0f73b37..fb2f968bbe 100644 --- a/src/Catalyst.Core.Modules.Ledger/Web3EthApi.cs +++ b/src/Catalyst.Core.Modules.Ledger/Web3EthApi.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,25 +22,126 @@ #endregion using System; +using System.Collections.Generic; +using System.Linq; +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.IO.Events; using Catalyst.Abstractions.Kvm; using Catalyst.Abstractions.Ledger; -using Nethermind.Store; +using Catalyst.Abstractions.Ledger.Models; +using Catalyst.Abstractions.Mempool; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Abstractions.Repository; +using Catalyst.Core.Abstractions.Sync; +using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Transaction; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Modules.Ledger.Repository; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Peer; +using Catalyst.Protocol.Transaction; +using Catalyst.Protocol.Wire; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using Lib.P2P; +using MultiFormats; +using Nethermind.Core.Crypto; +using Nethermind.State; -namespace Catalyst.Core.Modules.Ledger +namespace Catalyst.Core.Modules.Ledger { - public class Web3EthApi : IWeb3EthApi + public sealed class Web3EthApi : IWeb3EthApi { - public Web3EthApi(IDeltaExecutor deltaExecutor, IStateReader stateReader, IStateRootResolver stateRootResolver, IDeltaResolver deltaResolver) + private IMempool _mempoolRepository; + private readonly ITransactionRepository _receipts; + private readonly ITransactionReceivedEvent _transactionReceived; + private readonly IMapperProvider _mapperProvider; + public IHashProvider HashProvider { get; } + public IDfsService DfsService { get; } + public SyncState SyncState { get; } + private readonly MultiAddress _peerId; + + public Web3EthApi(IStateReader stateReader, + IDeltaResolver deltaResolver, + IDeltaCache deltaCache, + IDeltaExecutor executor, + IStorageProvider storageProvider, + IStateProvider stateProvider, + ITransactionRepository receipts, + ITransactionReceivedEvent transactionReceived, + IPeerRepository peerRepository, + IMempool mempoolRepository, + IDfsService dfsService, + IHashProvider hashProvider, + SyncState syncState, + IMapperProvider mapperProvider, + IPeerSettings peerSettings) { - DeltaExecutor = deltaExecutor ?? throw new ArgumentNullException(nameof(deltaExecutor)); + _receipts = receipts; + _transactionReceived = transactionReceived ?? throw new ArgumentNullException(nameof(transactionReceived)); + HashProvider = hashProvider; + _peerId = peerSettings.Address; + _mempoolRepository = mempoolRepository; + PeerRepository = peerRepository; + _mapperProvider = mapperProvider; + StateReader = stateReader ?? throw new ArgumentNullException(nameof(stateReader)); - StateRootResolver = stateRootResolver ?? throw new ArgumentNullException(nameof(stateRootResolver)); DeltaResolver = deltaResolver ?? throw new ArgumentNullException(nameof(deltaResolver)); + DeltaCache = deltaCache ?? throw new ArgumentNullException(nameof(deltaCache)); + Executor = executor ?? throw new ArgumentNullException(nameof(executor)); + StorageProvider = storageProvider ?? throw new ArgumentNullException(nameof(storageProvider)); + StateProvider = stateProvider ?? throw new ArgumentNullException(nameof(stateProvider)); + DfsService = dfsService; + SyncState = syncState; } - - public IDeltaExecutor DeltaExecutor { get; } + public IStateReader StateReader { get; } public IDeltaResolver DeltaResolver { get; } - public IStateRootResolver StateRootResolver { get; } + public IDeltaCache DeltaCache { get; } + + public IDeltaExecutor Executor { get; } + public IStorageProvider StorageProvider { get; } + public IStateProvider StateProvider { get; } + public IPeerRepository PeerRepository { get; } + + + public Keccak SendTransaction(PublicEntry publicEntry) + { + TransactionBroadcast broadcast = new TransactionBroadcast + { + PublicEntry = publicEntry + }; + + _transactionReceived.OnTransactionReceived(broadcast.ToProtocolMessage(_peerId), true); + + byte[] kvmAddressBytes = Keccak.Compute(publicEntry.SenderAddress.ToByteArray()).Bytes.AsSpan(12).ToArray(); + string hex = kvmAddressBytes.ToHexString() ?? throw new ArgumentNullException("kvmAddressBytes.ToHexString()"); + publicEntry.SenderAddress = kvmAddressBytes.ToByteString(); + + if (publicEntry.ReceiverAddress.Length == 1) + { + publicEntry.ReceiverAddress = ByteString.Empty; + } + + return publicEntry.GetHash(HashProvider); + } + + public IEnumerable GetPendingTransactions() + { + return _mempoolRepository.Service.GetAll().Select(x=>x.ToProtoBuff(_mapperProvider)); + } + + public TransactionReceipt FindReceipt(Keccak transactionHash) + { + return _receipts.TryFind(transactionHash, out TransactionReceipt receipt) ? receipt : default; + } + + public bool FindTransactionData(Keccak transactionHash, out Cid deltaHash, out Delta delta, out int index) + { + return _receipts.TryFind(transactionHash, out deltaHash, out delta, out index); + } } } diff --git a/src/Catalyst.Core.Modules.Mempool.Tests/Catalyst.Core.Modules.Mempool.Tests.csproj b/src/Catalyst.Core.Modules.Mempool.Tests/Catalyst.Core.Modules.Mempool.Tests.csproj index 606dd2e3dd..77757174a4 100644 --- a/src/Catalyst.Core.Modules.Mempool.Tests/Catalyst.Core.Modules.Mempool.Tests.csproj +++ b/src/Catalyst.Core.Modules.Mempool.Tests/Catalyst.Core.Modules.Mempool.Tests.csproj @@ -1,14 +1,25 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Mempool.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Mempool.Tests.snk true + + + 1701;1702;VSTHRD200;CS8002 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/Catalyst.Core.Modules.Mempool.Tests/IntegrationTests/TransactionBroadcastRepositoryTests.cs b/src/Catalyst.Core.Modules.Mempool.Tests/IntegrationTests/TransactionBroadcastRepositoryTests.cs deleted file mode 100644 index 193aaa7e82..0000000000 --- a/src/Catalyst.Core.Modules.Mempool.Tests/IntegrationTests/TransactionBroadcastRepositoryTests.cs +++ /dev/null @@ -1,197 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using Autofac; -using Catalyst.Core.Lib.DAO; -using Catalyst.Core.Lib.Repository; -using Catalyst.Protocol.Cryptography; -using Catalyst.Protocol.Network; -using Catalyst.Protocol.Wire; -using Catalyst.TestUtils; -using Catalyst.TestUtils.ProtocolHelpers; -using Catalyst.TestUtils.Repository; -using FluentAssertions; -using Microsoft.EntityFrameworkCore; -using SharpRepository.Repository; -using Xunit; -using Xunit.Abstractions; - -namespace Catalyst.Core.Modules.Mempool.Tests.IntegrationTests -{ - public sealed class TransactionBroadcastRepositoryTests : FileSystemBasedTest - { - private readonly IMapperProvider _mapperProvider; - - public static IEnumerable ModulesList => - new List - { - new object[] {new InMemoryTestModule()}, - new object[] {new MongoDbTestModule()} - }; - - public TransactionBroadcastRepositoryTests(ITestOutputHelper output) : base(output) - { - _mapperProvider = new TestMapperProvider(); - } - - private void TransactionBroadcastRepo_Can_Save_And_Retrieve() - { - using (var scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName)) - { - var transactBroadcastRepo = PopulateTransactBroadcastRepo(scope, out var criteriaId, - out var contractEntryDaoList, out var publicEntryDaoList); - - transactBroadcastRepo.Get(criteriaId).Id.Should().Be(criteriaId); - - transactBroadcastRepo.Get(criteriaId).ContractEntries.FirstOrDefault().Data - .Should().Be(contractEntryDaoList.FirstOrDefault().Data); - - transactBroadcastRepo.Get(criteriaId).PublicEntries.FirstOrDefault().Amount - .Should().Be(publicEntryDaoList.FirstOrDefault().Amount); - } - } - - private IRepository PopulateTransactBroadcastRepo(ILifetimeScope scope, - out string id, - out IEnumerable contractEntryDaoList, - out IEnumerable publicEntryDaoList) - { - var transactBroadcastRepo = scope.Resolve>(); - - var transactionBroadcastDao = TransactionHelper.GetPublicTransaction().ToDao(_mapperProvider); - id = transactionBroadcastDao.Id; - - transactionBroadcastDao.ContractEntries = ContractEntryHelper.GetContractEntriesDao(10); - contractEntryDaoList = transactionBroadcastDao.ContractEntries; - - transactionBroadcastDao.PublicEntries = PublicEntryHelper.GetPublicEntriesDao(10); - publicEntryDaoList = transactionBroadcastDao.PublicEntries; - - var signingContextDao = new SigningContextDao - { - NetworkType = NetworkType.Devnet, - SignatureType = SignatureType.TransactionPublic - }; - - transactionBroadcastDao.ConfidentialEntries = ConfidentialEntryHelper.GetConfidentialEntriesDao(10); - - transactionBroadcastDao.Signature = new SignatureDao - {RawBytes = "mplwifwfjfw", SigningContext = signingContextDao}; - - transactBroadcastRepo.Add(transactionBroadcastDao); - - return transactBroadcastRepo; - } - - private void TransactionBroadcast_Update_And_Retrieve() - { - using (var scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName)) - { - var transactBroadcastRepo = PopulateTransactBroadcastRepo(scope, out var criteriaId, - out _, out _); - - var retrievedTransactionDao = transactBroadcastRepo.Get(criteriaId); - retrievedTransactionDao.TimeStamp = new DateTime(1999, 2, 2); - transactBroadcastRepo.Update(retrievedTransactionDao); - - var retrievedTransactionDaoModified = transactBroadcastRepo.Get(criteriaId); - - var dateComparer = retrievedTransactionDaoModified.TimeStamp.Date.ToString("MM/dd/yyyy"); - - // ReSharper disable once SuspiciousTypeConversion.Global - // ReSharper disable once ReturnValueOfPureMethodIsNotUsed - dateComparer.Should()?.Equals("02/02/1999"); - } - } - - [Theory(Skip = "Setup to run in pipeline only")] - [Trait(Traits.TestType, Traits.E2EMongoDb)] - [MemberData(nameof(ModulesList))] - public void TransactionBroadcastRepo_All_Dbs_Can_Update_And_Retrieve(Module dbModule) - { - RegisterModules(dbModule); - - TransactionBroadcast_Update_And_Retrieve(); - } - - [Theory(Skip = "Setup to run in pipeline only")] - [Trait(Traits.TestType, Traits.E2EMongoDb)] - [MemberData(nameof(ModulesList))] - public void TransactionBroadcastRepo_All_Dbs_Can_Save_And_Retrieve(Module dbModule) - { - RegisterModules(dbModule); - - TransactionBroadcastRepo_Can_Save_And_Retrieve(); - } - - [Fact(Skip = "Microsoft DBs yet to be completed")] - [Trait(Traits.TestType, Traits.E2EMssql)] - public void TransactionBroadcastRepo_EfCore_Dbs_Update_And_Retrieve() - { - var connectionStr = ContainerProvider.ConfigurationRoot - .GetSection("CatalystNodeConfiguration:PersistenceConfiguration:repositories:efCore:connectionString") - .Value; - - RegisterModules(new EfCoreDbTestModule(connectionStr)); - - CheckForDatabaseCreation(); - - TransactionBroadcast_Update_And_Retrieve(); - } - - [Fact(Skip = "Microsoft DBs yet to be completed")] - [Trait(Traits.TestType, Traits.E2EMssql)] - public void TransactionBroadcastRepo_EfCore_Dbs_Can_Save_And_Retrieve() - { - var connectionStr = ContainerProvider.ConfigurationRoot - .GetSection("CatalystNodeConfiguration:PersistenceConfiguration:repositories:efCore:connectionString") - .Value; - - RegisterModules(new EfCoreDbTestModule(connectionStr)); - - CheckForDatabaseCreation(); - - TransactionBroadcastRepo_Can_Save_And_Retrieve(); - } - - private void CheckForDatabaseCreation() - { - using (var scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName)) - { - var contextDb = scope.Resolve(); - - ((DbContext) contextDb).Database.EnsureCreated(); - } - } - - private void RegisterModules(Module module) - { - ContainerProvider.ConfigureContainerBuilder(); - - ContainerProvider.ContainerBuilder.RegisterModule(module); - } - } -} diff --git a/src/Catalyst.Core.Modules.Mempool.Tests/IntegrationTests/TransactionRepositoryTests.cs b/src/Catalyst.Core.Modules.Mempool.Tests/IntegrationTests/TransactionRepositoryTests.cs new file mode 100644 index 0000000000..081046cfb7 --- /dev/null +++ b/src/Catalyst.Core.Modules.Mempool.Tests/IntegrationTests/TransactionRepositoryTests.cs @@ -0,0 +1,185 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using Autofac; +using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Cryptography; +using Catalyst.Core.Lib.DAO.Transaction; +using Catalyst.Core.Lib.Service; +using Catalyst.Protocol.Cryptography; +using Catalyst.Protocol.Network; +using Catalyst.Protocol.Transaction; +using Catalyst.TestUtils; +using Catalyst.TestUtils.ProtocolHelpers; +using Catalyst.TestUtils.Repository; +using FluentAssertions; +using Microsoft.EntityFrameworkCore; +using NUnit.Framework; +using SharpRepository.Repository; + +namespace Catalyst.Core.Modules.Mempool.Tests.IntegrationTests +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public sealed class TransactionRepositoryTests : FileSystemBasedTest + { + private IMapperProvider _mapperProvider; + + public static IEnumerable ModulesList => + new List + { + new object[] {new InMemoryTestModule()}, + new object[] {new MongoDbTestModule()} + }; + + [SetUp] + public void Init() + { + _mapperProvider = new TestMapperProvider(); + } + + private void TransactionRepository_Can_Save_And_Retrieve() + { + using (var scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName)) + { + var transactBroadcastRepo = PopulateTransactionRepository(scope, out var criteriaId, out var publicEntryDaoList); + + transactBroadcastRepo.Get(criteriaId).Id.Should().Be(criteriaId); + + transactBroadcastRepo.Get(criteriaId).Amount.Should().Be(publicEntryDaoList.FirstOrDefault().Amount); + } + } + + private IRepository PopulateTransactionRepository(ILifetimeScope scope, + out string id, + out IEnumerable publicEntryDaoList) + { + var transactionRepository = scope.Resolve>(); + + var transaction = TransactionHelper.GetPublicTransaction().PublicEntry.ToDao(_mapperProvider); + id = transaction.Id; + + publicEntryDaoList = PublicEntryHelper.GetPublicEntriesDao(10); + + transaction = publicEntryDaoList.First(); + + var signingContextDao = new SigningContextDao + { + NetworkType = NetworkType.Devnet, + SignatureType = SignatureType.TransactionPublic + }; + + transaction.Signature = new SignatureDao + { + RawBytes = "mplwifwfjfw", SigningContext = signingContextDao + }; + + transactionRepository.Add(transaction); + + return transactionRepository; + } + + private void Transaction_Update_And_Retrieve() + { + using (var scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName)) + { + var transactionRepository = PopulateTransactionRepository(scope, out var criteriaId, out _); + + var retrievedTransactionDao = transactionRepository.Get(criteriaId); + transactionRepository.Update(retrievedTransactionDao); + + var retrievedTransactionDaoModified = transactionRepository.Get(criteriaId); + } + } + + [Ignore("Setup to run in pipeline only")] + [Category(Traits.E2EMongoDb)] + [TestCase(nameof(ModulesList))] + public void TransactionRepository_All_Dbs_Can_Update_And_Retrieve(Autofac.Module dbModule) + { + RegisterModules(dbModule); + + Transaction_Update_And_Retrieve(); + } + + [Ignore("Setup to run in pipeline only")] + [Category(Traits.E2EMongoDb)] + [TestCase(nameof(ModulesList))] + public void TransactionBroadcastRepository_All_Dbs_Can_Save_And_Retrieve(Autofac.Module dbModule) + { + RegisterModules(dbModule); + + TransactionRepository_Can_Save_And_Retrieve(); + } + + [Ignore("Microsoft DBs yet to be completed")] + [Category(Traits.E2EMssql)] + public void TransactionRepository_EfCore_Dbs_Update_And_Retrieve() + { + var connectionStr = ContainerProvider.ConfigurationRoot + .GetSection("CatalystNodeConfiguration:PersistenceConfiguration:repositories:efCore:connectionString") + .Value; + + RegisterModules(new EfCoreDbTestModule(connectionStr)); + + CheckForDatabaseCreation(); + + Transaction_Update_And_Retrieve(); + } + + [Ignore("Microsoft DBs yet to be completed")] + [Category(Traits.E2EMssql)] + public void TransactionBroadcastRepository_EfCore_Dbs_Can_Save_And_Retrieve() + { + var connectionStr = ContainerProvider.ConfigurationRoot + .GetSection("CatalystNodeConfiguration:PersistenceConfiguration:repositories:efCore:connectionString") + .Value; + + RegisterModules(new EfCoreDbTestModule(connectionStr)); + + CheckForDatabaseCreation(); + + TransactionRepository_Can_Save_And_Retrieve(); + } + + private void CheckForDatabaseCreation() + { + using (var scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName)) + { + var contextDb = scope.Resolve(); + + ((DbContext) contextDb).Database.EnsureCreated(); + } + } + + private void RegisterModules(Autofac.Module module) + { + ContainerProvider.ConfigureContainerBuilder(); + + ContainerProvider.ContainerBuilder.RegisterModule(module); + } + } +} diff --git a/src/Catalyst.Core.Modules.Mempool.Tests/UnitTests/MempoolTests.cs b/src/Catalyst.Core.Modules.Mempool.Tests/UnitTests/MempoolTests.cs index c5e42e26db..26146d4470 100644 --- a/src/Catalyst.Core.Modules.Mempool.Tests/UnitTests/MempoolTests.cs +++ b/src/Catalyst.Core.Modules.Mempool.Tests/UnitTests/MempoolTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,65 +24,64 @@ using System; using System.Collections.Generic; using System.Linq; -using System.Text; -using Catalyst.Abstractions.Mempool.Repositories; +using Catalyst.Abstractions.Mempool.Services; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Extensions.Protocol.Wire; +using Catalyst.Core.Modules.Hashing; +using Catalyst.Protocol.Transaction; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using FluentAssertions; +using MultiFormats.Registry; using Nethermind.Dirichlet.Numerics; using NSubstitute; using NSubstitute.ExceptionExtensions; -using TheDotNetLeague.MultiFormats.MultiBase; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Mempool.Tests.UnitTests { public sealed class MempoolTests { - private readonly Mempool _memPool; + private Mempool _memPool; + private PublicEntryDao _mempoolItem; + private TestMapperProvider _mapperProvider; - private readonly TransactionBroadcastDao _transactionBroadcast; - private readonly TestMapperProvider _mapperProvider; - - public MempoolTests() + [SetUp] + public void Init() { - _memPool = new Mempool(Substitute.For>()); + new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + _memPool = new Mempool(Substitute.For>()); _mapperProvider = new TestMapperProvider(); - _transactionBroadcast = TransactionHelper - .GetPublicTransaction() - .ToDao(_mapperProvider); + _mempoolItem = TransactionHelper + .GetPublicTransaction().PublicEntry + .ToDao(_mapperProvider); } - private void AddKeyValueStoreEntryExpectation(TransactionBroadcastDao transaction) + private void AddKeyValueStoreEntryExpectation(PublicEntryDao mempoolItem) { - _memPool.Repository.ReadItem(Arg.Is(k => k == transaction.Signature.RawBytes)) - .Returns(transaction); + _memPool.Service.ReadItem(Arg.Is(k => k == mempoolItem.Id)) + .Returns(mempoolItem); - _memPool.Repository.TryReadItem(Arg.Is(k => k == transaction.Signature.RawBytes)) + _memPool.Service.TryReadItem(Arg.Is(k => k == mempoolItem.Id)) .Returns(true); } - [Fact] + [Test] public void Get_should_retrieve_a_saved_transaction() { - _memPool.Repository.CreateItem(_transactionBroadcast); - AddKeyValueStoreEntryExpectation(_transactionBroadcast); - - var mempoolDocument = _memPool.Repository.ReadItem(_transactionBroadcast.Signature.RawBytes) - .ToProtoBuff(_mapperProvider); - var expectedTransaction = _transactionBroadcast.ToProtoBuff(_mapperProvider); + _memPool.Service.CreateItem(_mempoolItem); + AddKeyValueStoreEntryExpectation(_mempoolItem); + + var mempoolDocument = _memPool.Service.ReadItem(_mempoolItem.Id).ToProtoBuff(_mapperProvider); + var expectedTransaction = _mempoolItem.ToProtoBuff(_mapperProvider); - mempoolDocument.PublicEntries.Single().Amount.ToUInt256().Should() - .Be(expectedTransaction.PublicEntries.Single().Amount.ToUInt256()); + mempoolDocument.Amount.ToUInt256().Should().Be(expectedTransaction.Amount.ToUInt256()); mempoolDocument.Signature.RawBytes.SequenceEqual(expectedTransaction.Signature.RawBytes).Should().BeTrue(); - mempoolDocument.Timestamp.Should().Be(expectedTransaction.Timestamp); - mempoolDocument.SummedEntryFees().Should().Be(expectedTransaction.SummedEntryFees()); + mempoolDocument.GasPrice.ToUInt256().Should().Be(expectedTransaction.GasPrice.ToUInt256()); } - [Fact] + [Test] public void Get_should_retrieve_saved_transaction_matching_their_keys() { const int numTx = 10; @@ -91,161 +90,154 @@ public void Get_should_retrieve_saved_transaction_matching_their_keys() for (var i = 0; i < numTx; i++) { - var signature = Encoding.UTF8.GetBytes($"key{i}").ToBase32(); - var mempoolDocument = _memPool.Repository.ReadItem(signature).ToProtoBuff(_mapperProvider); - mempoolDocument.PublicEntries.Single().Amount.ToUInt256().Should().Be((UInt256) i); + var mempoolDocument = _memPool.Service.ReadItem(documents[i].Id).ToProtoBuff(_mapperProvider); + mempoolDocument.Amount.ToUInt256().Should().Be((UInt256) i); } } - [Fact] + [Test] public void Delete_should_delete_all_transactions() { var keys = Enumerable.Range(0, 10).Select(i => i.ToString()).ToArray(); - _memPool.Repository.DeleteItem(keys); - _memPool.Repository.Received(1).DeleteItem(Arg.Is(s => s.SequenceEqual(keys))); + _memPool.Service.DeleteItem(keys); + _memPool.Service.Received(1).DeleteItem(Arg.Is(s => s.SequenceEqual(keys))); } - [Fact(Skip = "don't like testing we hit a logger")] + [Ignore("don't like testing we hit a logger")] public void Delete_should_log_deletion_errors() { var keys = Enumerable.Range(0, 3).Select(i => i.ToString()).ToArray(); - var connectTimeoutException = new TimeoutException("that mempool connection was too slow"); - _memPool.Repository.WhenForAnyArgs(t => t.DeleteItem(keys)) + TimeoutException connectTimeoutException = new("that mempool connection was too slow"); + _memPool.Service.WhenForAnyArgs(t => t.DeleteItem(keys)) .Throw(connectTimeoutException); - var result = _memPool.Repository.DeleteItem(keys); + var result = _memPool.Service.DeleteItem(keys); result.Should().BeFalse(); } - [Fact] + [Test] public void Get_When_Key_Not_In_Store_Should_Throw() { - _memPool.Repository.ReadItem(Arg.Any()).ThrowsForAnyArgs(new KeyNotFoundException()); - new Action(() => _memPool.Repository.ReadItem("Signature that doesn't exist")) + _memPool.Service.ReadItem(Arg.Any()).ThrowsForAnyArgs(new KeyNotFoundException()); + new Action(() => _memPool.Service.ReadItem("Signature that doesn't exist")) .Should().Throw(); } - [Fact] + [Test] public void SaveMempoolDocument_Should_Not_Override_Existing_Record() { // this test seems pointless like this - var expectedAmount = _transactionBroadcast - .ToProtoBuff(_mapperProvider) - .PublicEntries.Single().Amount; + var expectedAmount = _mempoolItem.ToProtoBuff(_mapperProvider).Amount; - _memPool.Repository.CreateItem(Arg.Is(_transactionBroadcast)) + _memPool.Service.CreateItem(Arg.Is(_mempoolItem)) .Returns(true); - var saved = _memPool.Repository.CreateItem(_transactionBroadcast); + var saved = _memPool.Service.CreateItem(_mempoolItem); saved.Should().BeTrue(); - var overridingTransaction = _transactionBroadcast - .ToProtoBuff(_mapperProvider).Clone(); + var overridingTransaction = _mempoolItem.ToProtoBuff(_mapperProvider).Clone(); - overridingTransaction.PublicEntries.Single().Amount = - (expectedAmount.ToUInt256() + (UInt256) 100).ToUint256ByteString(); + overridingTransaction.Amount = (expectedAmount.ToUInt256() + (UInt256) 100).ToUint256ByteString(); - var overridingTransactionDao = overridingTransaction.ToDao(_mapperProvider); - _memPool.Repository.CreateItem(Arg.Is(overridingTransactionDao)) - .Returns(false); - var overriden = _memPool.Repository.CreateItem(overridingTransactionDao); + var overridingTransactionDao = overridingTransaction.ToDao(_mapperProvider); + _memPool.Service.CreateItem(Arg.Is(overridingTransactionDao)).Returns(false); + var overriden = _memPool.Service.CreateItem(overridingTransactionDao); overriden.Should().BeFalse(); - _memPool.Repository.TryReadItem(Arg.Is(_transactionBroadcast.Signature.RawBytes)) + _memPool.Service.TryReadItem(Arg.Is(_mempoolItem.Signature.RawBytes)) .Returns(true); - var retrievedTransaction = _memPool.Repository.TryReadItem(_transactionBroadcast.Signature.RawBytes); + var retrievedTransaction = _memPool.Service.TryReadItem(_mempoolItem.Signature.RawBytes); retrievedTransaction.Should().BeTrue(); } - [Fact] + [Test] public void SaveMempoolDocument_Should_Return_False_And_Log_On_Store_Exception() { - var exception = new TimeoutException("underlying store is not connected"); - _memPool.Repository.TryReadItem(default) + TimeoutException exception = new("underlying store is not connected"); + _memPool.Service.TryReadItem(default) .ThrowsForAnyArgs(exception); - var saved = _memPool.Repository.CreateItem(_transactionBroadcast); + var saved = _memPool.Service.CreateItem(_mempoolItem); saved.Should().BeFalse(); } - [Fact] + [Test] public void SaveMempoolDocument_Should_Throw_On_Document_With_Null_Transaction() { - _transactionBroadcast.Signature.RawBytes = null; + _mempoolItem.Signature.RawBytes = null; - _memPool.Repository.CreateItem(_transactionBroadcast).Throws(); + _memPool.Service.CreateItem(_mempoolItem).Throws(); - new Action(() => _memPool.Repository.CreateItem(_transactionBroadcast)) + new Action(() => _memPool.Service.CreateItem(_mempoolItem)) .Should().Throw() .And.Message.Should().Contain("cannot be null"); } - [Fact] + [Test] public void SaveMempoolDocument_Should_Throw_On_Null_Document() { - _memPool.Repository.CreateItem(null).Throws(); + _memPool.Service.CreateItem(null).Throws(); - new Action(() => _memPool.Repository.CreateItem(null)) + new Action(() => _memPool.Service.CreateItem(null)) .Should().Throw() .And.Message.Should().Contain("cannot be null"); // transaction is null so do not insert } - [Fact] + [Test] public void GetMempoolContent_should_return_all_documents_from_mempool() { var documentCount = 13; var mempoolDocs = GetTestingMempoolDocuments(documentCount); - _memPool.Repository.GetAll().Returns(mempoolDocs); + _memPool.Service.GetAll().Returns(mempoolDocs); - var content = _memPool.Repository.GetAll().ToList(); + var content = _memPool.Service.GetAll().ToList(); - _memPool.Repository.ReceivedWithAnyArgs(1).GetAll(); + _memPool.Service.ReceivedWithAnyArgs(1).GetAll(); content.Count.Should().Be(documentCount); content.Should().BeEquivalentTo(mempoolDocs); } - private List GetTestingMempoolDocuments(int documentCount) + private List GetTestingMempoolDocuments(int documentCount) { return Enumerable.Range(0, documentCount).Select(i => - TransactionHelper.GetPublicTransaction((uint) i, signature: $"key{i}").ToDao(_mapperProvider)) - .ToList(); + TransactionHelper.GetPublicTransaction((uint) i, signature: $"key{i}").ToDao(_mapperProvider)).Select(x => x.PublicEntry).ToList(); } - [Fact] + [Test] public void GetMempoolContentEncoded_should_return_an_array_of_bytes_of_strings_of_all_transactions() { var documentCount = 7; var mempoolDocs = GetTestingMempoolDocuments(documentCount); - _memPool.Repository.GetAll().Returns(mempoolDocs); + _memPool.Service.GetAll().Returns(mempoolDocs); - var content = _memPool.Repository.GetAll().ToList(); + var content = _memPool.Service.GetAll().ToList(); - _memPool.Repository.ReceivedWithAnyArgs(1).GetAll(); + _memPool.Service.ReceivedWithAnyArgs(1).GetAll(); content.Count.Should().Be(documentCount); content.Should().BeEquivalentTo(mempoolDocs); } - [Fact] + [Test] public void ContainsDocument_Should_Return_True_On_Known_DocumentId() { - AddKeyValueStoreEntryExpectation(_transactionBroadcast); - _memPool.Repository.TryReadItem(_transactionBroadcast.Signature.RawBytes).Should().BeTrue(); + AddKeyValueStoreEntryExpectation(_mempoolItem); + _memPool.Service.TryReadItem(_mempoolItem.Id).Should().BeTrue(); } - [Fact] + [Test] public void ContainsDocument_Should_Return_False_On_Unknown_DocumentId() { var unknownTransaction = "key not in the mempool"; - _memPool.Repository.TryReadItem(unknownTransaction).Should().BeFalse(); + _memPool.Service.TryReadItem(unknownTransaction).Should().BeFalse(); } } } diff --git a/src/Catalyst.Core.Modules.Mempool/Catalyst.Core.Modules.Mempool.csproj b/src/Catalyst.Core.Modules.Mempool/Catalyst.Core.Modules.Mempool.csproj index c208237250..d37f8d9caf 100644 --- a/src/Catalyst.Core.Modules.Mempool/Catalyst.Core.Modules.Mempool.csproj +++ b/src/Catalyst.Core.Modules.Mempool/Catalyst.Core.Modules.Mempool.csproj @@ -1,12 +1,15 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Mempool - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Mempool.snk true + + 1701;1702;CS8002 + diff --git a/src/Catalyst.Core.Modules.Mempool/Mempool.cs b/src/Catalyst.Core.Modules.Mempool/Mempool.cs index 29335138dc..e86f2013bb 100644 --- a/src/Catalyst.Core.Modules.Mempool/Mempool.cs +++ b/src/Catalyst.Core.Modules.Mempool/Mempool.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,8 +22,8 @@ #endregion using Catalyst.Abstractions.Mempool; -using Catalyst.Abstractions.Mempool.Repositories; -using Catalyst.Core.Lib.DAO; +using Catalyst.Abstractions.Mempool.Services; +using Catalyst.Core.Lib.DAO.Transaction; using Dawn; namespace Catalyst.Core.Modules.Mempool @@ -31,15 +31,15 @@ namespace Catalyst.Core.Modules.Mempool /// /// Mempool class wraps around a IKeyValueStore /// - public sealed class Mempool : IMempool + public sealed class Mempool : IMempool { - public IMempoolRepository Repository { get; } + public IMempoolService Service { get; } /// - public Mempool(IMempoolRepository transactionStore) + public Mempool(IMempoolService mempoolService) { - Guard.Argument(transactionStore, nameof(transactionStore)).NotNull(); - Repository = transactionStore; + Guard.Argument(mempoolService, nameof(mempoolService)).NotNull(); + Service = mempoolService; } } } diff --git a/src/Catalyst.Core.Modules.Mempool/MempoolModule.cs b/src/Catalyst.Core.Modules.Mempool/MempoolModule.cs index dd3991c9d0..033279adee 100644 --- a/src/Catalyst.Core.Modules.Mempool/MempoolModule.cs +++ b/src/Catalyst.Core.Modules.Mempool/MempoolModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,10 +23,11 @@ using Autofac; using Catalyst.Abstractions.Mempool; -using Catalyst.Abstractions.Mempool.Repositories; -using Catalyst.Core.Lib.DAO; +using Catalyst.Abstractions.Mempool.Services; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Modules.Mempool.Repositories; using SharpRepository.InMemoryRepository; +using SharpRepository.MongoDbRepository; using SharpRepository.Repository; namespace Catalyst.Core.Modules.Mempool @@ -35,12 +36,15 @@ public class MempoolModule : Module { protected override void Load(ContainerBuilder builder) { - builder.Register(c => new InMemoryRepository()) - .As>() + builder.Register(c => new MongoDbRepository()) + .As>() + .SingleInstance(); + //builder.Register(c => new InMemoryRepository()) + // .As>() + // .SingleInstance(); + builder.RegisterType().As>() .SingleInstance(); - builder.RegisterType().As>() - .SingleInstance(); - builder.RegisterType().As>().SingleInstance(); + builder.RegisterType().As>().SingleInstance(); } } } diff --git a/src/Catalyst.Core.Modules.Mempool/Repositories/MempoolRepository.cs b/src/Catalyst.Core.Modules.Mempool/Repositories/MempoolService.cs similarity index 51% rename from src/Catalyst.Core.Modules.Mempool/Repositories/MempoolRepository.cs rename to src/Catalyst.Core.Modules.Mempool/Repositories/MempoolService.cs index 94e60c4166..5f8f576d4b 100644 --- a/src/Catalyst.Core.Modules.Mempool/Repositories/MempoolRepository.cs +++ b/src/Catalyst.Core.Modules.Mempool/Repositories/MempoolService.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,57 +22,70 @@ #endregion using System; -using Catalyst.Abstractions.Mempool.Repositories; -using Catalyst.Core.Lib.DAO; -using Catalyst.Core.Lib.Repository; +using System.Collections.Generic; +using Catalyst.Abstractions.Mempool.Services; +using Catalyst.Core.Lib.DAO.Transaction; using Dawn; using Serilog; using SharpRepository.Repository; namespace Catalyst.Core.Modules.Mempool.Repositories { - public class MempoolRepository : RepositoryWrapper, - IMempoolRepository + public class MempoolService : IMempoolService { - public MempoolRepository(IRepository repository) : base(repository) { } + private readonly IRepository _repository; + + public MempoolService(IRepository repository) + { + _repository = repository; + } + + public IEnumerable GetAll() + { + return _repository.GetAll(); + } /// - public bool TryReadItem(string signature) + public bool TryReadItem(string id) { - Guard.Argument(signature, nameof(signature)).NotNull(); - return Repository.TryGet(signature, out _); + Guard.Argument(id, nameof(id)).NotNull(); + return _repository.TryGet(id, out _); } - public TransactionBroadcastDao ReadItem(string signature) + public PublicEntryDao ReadItem(string id) { - Guard.Argument(signature, nameof(signature)).NotNull(); - return Repository.Get(signature); + Guard.Argument(id, nameof(id)).NotNull(); + return _repository.Get(id); + } + + public void Delete(IEnumerable mempoolItems) + { + _repository.Delete(mempoolItems); } /// - public bool DeleteItem(params string[] transactionSignatures) + public bool DeleteItem(params string[] ids) { try { - Repository.Delete(transactionSignatures); + _repository.Delete(ids); } catch (Exception exception) { - Log.Logger.Error(exception, "Failed to delete transactions from the mempool {transactionSignatures}", - transactionSignatures); + Log.Logger.Error(exception, "Failed to delete transactions from the mempool {ids}", ids); return false; } return true; } - public bool CreateItem(TransactionBroadcastDao transactionBroadcast) + public bool CreateItem(PublicEntryDao mempoolItem) { - Guard.Argument(transactionBroadcast.Signature, nameof(transactionBroadcast.Signature)).NotNull(); + Guard.Argument(mempoolItem.Id, nameof(mempoolItem.Id)).NotNull(); try { - Repository.Add(transactionBroadcast); + _repository.Add(mempoolItem); } catch (Exception e) { @@ -82,5 +95,10 @@ public bool CreateItem(TransactionBroadcastDao transactionBroadcast) return true; } + + public void Dispose() + { + _repository.Dispose(); + } } } diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.csproj b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.csproj index 1eebde2bb5..befc8976fa 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.csproj +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.csproj @@ -1,15 +1,25 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.snk true - + + 1701;1702;VSTHRD200;CS8002 + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/IntegrationTests/HastingsCareTakerTests.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/IntegrationTests/HastingsCareTakerTests.cs index bcd9cfb1e7..1314b3bbfc 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/IntegrationTests/HastingsCareTakerTests.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/IntegrationTests/HastingsCareTakerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,46 +27,44 @@ using Catalyst.Protocol.Peer; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; +using MultiFormats; +using NUnit.Framework; namespace Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.IntegrationTests { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class HastingsCareTakerTests { - private readonly PeerId _ownNode; + private readonly MultiAddress _ownNode; - public HastingsCareTakerTests() - { - _ownNode = PeerIdHelper.GetPeerId("own_node"); - } - - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + public HastingsCareTakerTests() { _ownNode = MultiAddressHelper.GetAddress("own_node"); } + + [Test] public void Can_Add_New_Mementos_To_Caretaker() { var careTaker = new HastingsCareTaker(); var stack = new Stack(); stack.Push(DiscoveryHelper.SubMemento(_ownNode)); - + var history = DiscoveryHelper.MockMementoHistory(stack); - + history.ToList().ForEach(m => careTaker.Add(m)); careTaker.HastingMementoList.Should().Contain(history); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Taking_From_Memento_List_Takes_LIFO() { var careTaker = new HastingsCareTaker(); var stack = new Stack(); stack.Push(DiscoveryHelper.SubMemento(_ownNode)); - + var history = DiscoveryHelper.MockMementoHistory(stack); - + history.ToList().ForEach(m => careTaker.Add(m)); careTaker.Get().Should().Be(history.Last()); diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/IntegrationTests/HastingsDiscoveryTests.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/IntegrationTests/HastingsDiscoveryTests.cs index 34d8775c08..8191d1bc07 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/IntegrationTests/HastingsDiscoveryTests.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/IntegrationTests/HastingsDiscoveryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -47,30 +47,33 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; +using MultiFormats; namespace Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.IntegrationTests { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class HastingsDiscoveryTests : FileSystemBasedTest { - public HastingsDiscoveryTests(ITestOutputHelper output) : base(output) + [SetUp] + public void Init() { + this.Setup(TestContext.CurrentContext); _testScheduler = new TestScheduler(); _settings = PeerSettingsHelper.TestPeerSettings(); - _ownNode = PeerIdHelper.GetPeerId("ownNode"); + _ownNode = MultiAddressHelper.GetAddress("ownNode"); ContainerProvider.ConfigureContainerBuilder(true, true); _logger = ContainerProvider.Container.Resolve(); } - private readonly TestScheduler _testScheduler; - private readonly IPeerSettings _settings; - private readonly PeerId _ownNode; - private readonly ILogger _logger; + private TestScheduler _testScheduler; + private IPeerSettings _settings; + private MultiAddress _ownNode; + private ILogger _logger; - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Evicted_Known_Ping_Message_Sets_Contacted_Neighbour_As_UnReachable_And_Can_RollBack_State() { var cacheEntriesByRequest = new Dictionary(); @@ -102,7 +105,7 @@ public void Evicted_Known_Ping_Message_Sets_Contacted_Neighbour_As_UnReachable_A stateCandidate.Neighbours.ToList().ForEach(n => { - _logger.Debug("Setting up neighbour {neighbour}", n.PeerId); + _logger.Debug("Setting up neighbour {neighbour}", n.Address); _logger.Debug("Adding eviction callbacks expectation for correlationId {correlationId}", n.DiscoveryPingCorrelationId); @@ -117,7 +120,7 @@ public void Evicted_Known_Ping_Message_Sets_Contacted_Neighbour_As_UnReachable_A { Content = new PingRequest().ToProtocolMessage(_ownNode, n.DiscoveryPingCorrelationId), - Recipient = n.PeerId + Recipient = n.Address }; correlatableMessages.Add(msg); @@ -148,9 +151,9 @@ public void Evicted_Known_Ping_Message_Sets_Contacted_Neighbour_As_UnReachable_A .PostEvictionCallbacks[0] .EvictionCallback .Invoke( - n.PeerId, + n.Address, correlatableMessages.Single(i => - i.Recipient.Equals(n.PeerId)), + i.Recipient.Equals(n.Address)), EvictionReason.Expired, new object() ); @@ -180,8 +183,7 @@ public void Evicted_Known_Ping_Message_Sets_Contacted_Neighbour_As_UnReachable_A } } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Expected_Ping_Response_From_All_Contacted_Nodes_Produces_Valid_State_Candidate() { var seedState = DiscoveryHelper.SubSeedState(_ownNode, _settings); @@ -219,7 +221,7 @@ public void Expected_Ping_Response_From_All_Contacted_Nodes_Produces_Valid_State stateCandidate.Neighbours.ToList().ForEach(i => { var dto = new PeerClientMessageDto(new PingResponse(), - stateCandidate.Neighbours.FirstOrDefault()?.PeerId, + stateCandidate.Neighbours.FirstOrDefault()?.Address, stateCandidate.Neighbours.FirstOrDefault()?.DiscoveryPingCorrelationId ); @@ -235,18 +237,17 @@ public void Expected_Ping_Response_From_All_Contacted_Nodes_Produces_Valid_State { walker.StepProposal.Neighbours .Where(n => n.StateTypes == NeighbourStateTypes.Responsive) - .Select(i => i.PeerId) + .Select(i => i.Address) .Should() .BeSubsetOf( stateCandidate.Neighbours - .Select(i => i.PeerId) + .Select(i => i.Address) ); } } } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Expected_Ping_Response_Sets_Neighbour_As_Reachable() { var seedState = DiscoveryHelper.SubSeedState(_ownNode, _settings); @@ -286,7 +287,7 @@ public void Expected_Ping_Response_Sets_Neighbour_As_Reachable() using (walker.DiscoveryStream.Subscribe(streamObserver.OnNext)) { var pingDto = new PeerClientMessageDto(new PingResponse(), - stateCandidate.Neighbours.FirstOrDefault()?.PeerId, + stateCandidate.Neighbours.FirstOrDefault()?.Address, stateCandidate.Neighbours.FirstOrDefault()?.DiscoveryPingCorrelationId ); @@ -300,9 +301,9 @@ public void Expected_Ping_Response_Sets_Neighbour_As_Reachable() var foundResponsiveNeighbour = walker.StepProposal.Neighbours .Where(n => n.StateTypes == NeighbourStateTypes.Responsive) - .Select(n => n.PeerId) + .Select(n => n.Address) .Contains(pingDto.Sender); - + foundResponsiveNeighbour.Should().BeTrue(); } } diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/DiscoveryTestBuilder.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/DiscoveryTestBuilder.cs index 56124e1894..a04ca252ac 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/DiscoveryTestBuilder.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/DiscoveryTestBuilder.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,10 +32,11 @@ using Catalyst.Abstractions.P2P.Discovery; using Catalyst.Abstractions.P2P.IO; using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; +using Catalyst.Abstractions.P2P.Protocols; using Catalyst.Abstractions.Util; using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Core.Lib.P2P.IO.Observers; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.P2P.ReputationSystem; using Catalyst.Core.Lib.Util; using Catalyst.Protocol.Peer; @@ -43,6 +44,8 @@ using Microsoft.Extensions.Caching.Memory; using NSubstitute; using Serilog; +using MultiFormats; +using Catalyst.Core.Lib.Abstractions.P2P.IO; namespace Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.UnitTests { @@ -125,9 +128,10 @@ public DiscoveryTestBuilder WithDns(IDns dnsClient = default, { _dnsClient = dnsClient == default(IDns) && mock == false ? Substitute.For() - : DiscoveryHelper.MockDnsClient(_peerSettings = _peerSettings == null && peerSettings == default(IPeerSettings) - ? PeerSettingsHelper.TestPeerSettings() - : peerSettings); + : DiscoveryHelper.MockDnsClient(_peerSettings = + _peerSettings == null && peerSettings == default(IPeerSettings) + ? PeerSettingsHelper.TestPeerSettings() + : peerSettings); return this; } @@ -185,7 +189,7 @@ private IPeerClientObservable GetPeerClientObservable(ILogger logger, MemberInfo switch (type.Name) { case nameof(PingResponseObserver): - return new PingResponseObserver(logger, Substitute.For()); + return new PingResponseObserver(Substitute.For(), logger); case nameof(GetNeighbourResponseObserver): return new GetNeighbourResponseObserver(logger); @@ -209,7 +213,7 @@ public DiscoveryTestBuilder WithBurn(int burnInPeriod = 0) public DiscoveryTestBuilder WithCurrentStep(IHastingsMemento currentStep = default, bool mock = false, - PeerId peer = default, + MultiAddress peer = default, INeighbours neighbours = default) { if (_careTaker == null) @@ -228,7 +232,7 @@ public DiscoveryTestBuilder WithCurrentStep(IHastingsMemento currentStep = defau public DiscoveryTestBuilder WithStepProposal(IHastingsOriginator stateCandidate = default, bool mock = false, - PeerId peer = default, + MultiAddress peer = default, INeighbours neighbours = default, ICorrelationId expectedPnr = default) { @@ -271,7 +275,7 @@ private HastingDiscoveryTest(ILogger logger = default, peerSettings ?? PeerSettingsHelper.TestPeerSettings(), peerClient ?? Substitute.For(), peerMessageCorrelationManager ?? DiscoveryHelper.MockCorrelationManager(scheduler), - cancellationTokenProvider ?? new CancellationTokenProvider(), + cancellationTokenProvider ?? new CancellationTokenProvider(true), peerClientObservables, autoStart, peerDiscoveryBurnIn, diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsCareTakerTests.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsCareTakerTests.cs index 85feb65191..ff876dc6a7 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsCareTakerTests.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsCareTakerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,51 +27,51 @@ using Catalyst.TestUtils; using FluentAssertions; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.UnitTests { public sealed class HastingsCareTakerTests { - [Fact] + [Test] public void Can_Get_From_HastingMementoList() { var careTaker = new HastingsCareTaker(); var subbedMemento = Substitute.For(); - + careTaker.Add(subbedMemento); careTaker.HastingMementoList.Should().Contain(subbedMemento); } - - [Fact] + + [Test] public void Can_Get_Nothing_From_Empty_HastingMementoList() { var careTaker = new HastingsCareTaker(); careTaker.HastingMementoList.Should().BeEmpty(); } - - [Fact] + + [Test] public void Care_Taker_Can_Add_State_To_CareTaker() { var careTaker = new HastingsCareTaker(); var subbedMemento = Substitute.For(); - + careTaker.Add(subbedMemento); // ReSharper disable once ReturnValueOfPureMethodIsNotUsed careTaker.HastingMementoList?.Contains(subbedMemento); careTaker.HastingMementoList.Should().HaveCount(1); } - - [Fact] + + [Test] public void Care_Taker_Can_Get_Last_State() { var careTaker = new HastingsCareTaker(); var subbedMemento = Substitute.For(); - + careTaker.Add(subbedMemento); careTaker.Add(subbedMemento); careTaker.HastingMementoList.Should().HaveCount(2); @@ -79,51 +79,51 @@ public void Care_Taker_Can_Get_Last_State() previousState.Should().BeSameAs(subbedMemento); careTaker.HastingMementoList.Should().HaveCount(1); } - - [Fact] + + [Test] public void Care_Taker_Should_Throw_Exception_Trying_To_Take_Last_State_From_Empty_CareTaker() { var careTaker = new HastingsCareTaker(); - + Assert.Throws(() => { careTaker.Get(); }); } - - [Fact] + + [Test] public void Can_Never_Take_Last_State_From_CareTaker() { var careTaker = new HastingsCareTaker(); var subbedMemento = Substitute.For(); - + careTaker.Add(subbedMemento); - + careTaker.Get(); careTaker.HastingMementoList.Count.Should().Be(1); } - - [Fact] + + [Test] public void Can_LIFO_When_History_N_Plus2() { var careTaker = new HastingsCareTaker(); var subbedMemento1 = Substitute.For(); - subbedMemento1.Peer.Returns(PeerIdHelper.GetPeerId("step1")); + subbedMemento1.Peer.Returns(MultiAddressHelper.GetAddress("step1")); careTaker.Add(subbedMemento1); var subbedMemento2 = Substitute.For(); - subbedMemento2.Peer.Returns(PeerIdHelper.GetPeerId("step2")); + subbedMemento2.Peer.Returns(MultiAddressHelper.GetAddress("step2")); careTaker.Add(subbedMemento2); - + var subbedMemento3 = Substitute.For(); - subbedMemento3.Peer.Returns(PeerIdHelper.GetPeerId("step3")); + subbedMemento3.Peer.Returns(MultiAddressHelper.GetAddress("step3")); careTaker.Add(subbedMemento3); - + var lastState1 = careTaker.Get(); lastState1.Should().Be(subbedMemento3); careTaker.HastingMementoList.Count.Should().Be(2); careTaker.HastingMementoList.First().Should().Be(subbedMemento2); - + var lastState2 = careTaker.Get(); lastState2.Should().Be(subbedMemento2); careTaker.HastingMementoList.Count.Should().Be(1); diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsDiscoveryTests.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsDiscoveryTests.cs index cb5a4a8fd1..7d3cf0da3f 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsDiscoveryTests.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsDiscoveryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,7 +29,6 @@ using System.Threading; using System.Threading.Tasks; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.P2P.Discovery; using Catalyst.Abstractions.P2P.IO.Messaging.Dto; @@ -43,12 +42,12 @@ using Catalyst.Core.Lib.Util; using Catalyst.Protocol.Wire; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; -using Xunit; +using NUnit.Framework; +using MultiFormats; namespace Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.UnitTests { @@ -56,16 +55,16 @@ public sealed class HastingsDiscoveryTests { private readonly TestScheduler _testScheduler; private readonly IPeerSettings _settings; - private readonly PeerId _ownNode; + private readonly MultiAddress _ownNode; public HastingsDiscoveryTests() { _testScheduler = new TestScheduler(); _settings = PeerSettingsHelper.TestPeerSettings(); - _ownNode = PeerIdHelper.GetPeerId("ownNode"); + _ownNode = MultiAddressHelper.GetAddress("ownNode"); } - [Fact] + [Test] public void Can_Store_Peer_After_Burn_In() { var discoveryTestBuilder = new DiscoveryTestBuilder() @@ -87,28 +86,25 @@ public void Can_Store_Peer_After_Burn_In() walker.GetBurnInValue() .Should() .Be(5); - + Enumerable.Range(0, 5).ToList().ForEach(i => { walker.TestStorePeer(Substitute.For()); - + walker.PeerRepository .Received(0) - .Add(Arg.Any()); + .Add(Arg.Any()); }); - - Enumerable.Range(0, 5).ToList().ForEach(i => - { - walker.TestStorePeer(Substitute.For()); - }); - + + Enumerable.Range(0, 5).ToList().ForEach(i => { walker.TestStorePeer(Substitute.For()); }); + walker.PeerRepository .Received(5) - .Add(Arg.Any()); + .Add(Arg.Any()); } } - [Fact] + [Test] public void Cant_Store_Peer_During_Burn_In() { var discoveryTestBuilder = new DiscoveryTestBuilder() @@ -130,22 +126,22 @@ public void Cant_Store_Peer_During_Burn_In() walker.GetBurnInValue() .Should() .Be(10); - + walker.TestStorePeer(Substitute.For()); - + walker.PeerRepository .Received(0) .Add(Arg.Any()); } } - [Fact] + [Test] public void Can_WalkForward_With_Valid_Candidate() { var knownStepPid = - PeerIdHelper.GetPeerId("hey_its_jimmys_brother_the_guy_with_the_beautiful_voice"); + MultiAddressHelper.GetAddress("hey_its_jimmys_brother_the_guy_with_the_beautiful_voice"); var knownNextCandidate = - PeerIdHelper.GetPeerId("these_eyes...."); + MultiAddressHelper.GetAddress("these_eyes...."); var discoveryTestBuilder = new DiscoveryTestBuilder() .WithLogger() @@ -169,27 +165,28 @@ public void Can_WalkForward_With_Valid_Candidate() walker.CurrentStep.Peer .Should() .Be(knownStepPid); - + walker.WalkForward(); - + walker.CurrentStep.Peer .Should() .Be(knownNextCandidate); } } - [Fact] + [Test] public void Can_Not_WalkForward_With_InValid_Candidate() { - var proposalCandidateId = PeerIdHelper.GetPeerId("these_eyes...."); + var proposalCandidateId = MultiAddressHelper.GetAddress("these_eyes...."); var knownStepPid = - PeerIdHelper.GetPeerId("hey_its_jimmys_brother_the_guy_with_the_beautiful_voice"); + MultiAddressHelper.GetAddress("hey_its_jimmys_brother_the_guy_with_the_beautiful_voice"); var knownStepNeighbours = new Neighbours(new[] {new Neighbour(proposalCandidateId)}); var latestStep = new HastingsMemento(knownStepPid, knownStepNeighbours); var proposal = Substitute.For(); - var unresponsiveNeighbours = DiscoveryHelper.MockNeighbours(Constants.NumberOfRandomPeers, NeighbourStateTypes.UnResponsive); + var unresponsiveNeighbours = + DiscoveryHelper.MockNeighbours(Constants.NumberOfRandomPeers, NeighbourStateTypes.UnResponsive); proposal.Neighbours.Returns(unresponsiveNeighbours); proposal.Peer.Returns(proposalCandidateId); @@ -212,16 +209,16 @@ public void Can_Not_WalkForward_With_InValid_Candidate() walker.CurrentStep.Peer .Should() .Be(knownStepPid); - + walker.WalkForward(); - + walker.CurrentStep.Peer .Should() .Be(knownStepPid); } } - [Fact] + [Test] public void HasValidCandidate_Can_Validate_Correct_State() { var discoveryTestBuilder = new DiscoveryTestBuilder() @@ -247,7 +244,7 @@ public void HasValidCandidate_Can_Validate_Correct_State() } } - [Fact] + [Test] public void HasValidCandidate_Can_Detect_Invalid_State() { var discoveryTestBuilder = new DiscoveryTestBuilder() @@ -272,11 +269,11 @@ public void HasValidCandidate_Can_Detect_Invalid_State() walker.StepProposal.HasValidCandidate().Should().BeFalse(); } } - - [Fact] + + [Test] public async Task Can_Throw_Exception_In_WalkBack_When_Last_State_Has_No_Neighbours_To_Continue_Walk_Forward() { - var ctp = new CancellationTokenProvider(); + var ctp = new CancellationTokenProvider(true); var discoveryTestBuilder = new DiscoveryTestBuilder() .WithLogger() .WithScheduler(_testScheduler) @@ -293,7 +290,7 @@ public async Task Can_Throw_Exception_In_WalkBack_When_Last_State_Has_No_Neighbo using (var walker = discoveryTestBuilder.Build()) { - await Assert.ThrowsAsync(async () => + Assert.ThrowsAsync(async () => { await walker.DiscoveryAsync(); Thread.Sleep(2); @@ -302,7 +299,7 @@ await Assert.ThrowsAsync(async () => } } - [Fact] + [Test] public void Can_Get_State_From_CareTaker() { var discoveryTestBuilder = new DiscoveryTestBuilder() @@ -325,7 +322,7 @@ public void Can_Get_State_From_CareTaker() } } - [Fact] + [Test] public void Discovery_Can_Build_Initial_State_From_SeedNodes() { var discoveryTestBuilder = new DiscoveryTestBuilder() @@ -345,14 +342,17 @@ public void Discovery_Can_Build_Initial_State_From_SeedNodes() { walker.StepProposal.Neighbours .Should() - .HaveCount(Constants.NumberOfRandomPeers); // http://giphygifs.s3.amazonaws.com/media/9MFsKQ8A6HCN2/giphy.gif + .HaveCount(Constants + .NumberOfRandomPeers); // http://giphygifs.s3.amazonaws.com/media/9MFsKQ8A6HCN2/giphy.gif } } [Theory] - [InlineData(typeof(PingResponse), typeof(PingResponseObserver), "OnPingResponse")] - [InlineData(typeof(PeerNeighborsResponse), typeof(GetNeighbourResponseObserver), "OnPeerNeighbourResponse")] - public void Can_Merge_PeerClientObservable_Stream_And_Read_Items_Pushed_On_Separate_Streams(Type discoveryMessage, Type observer, string logMsg) + [TestCase(typeof(PingResponse), typeof(PingResponseObserver), "OnPingResponse")] + [TestCase(typeof(PeerNeighborsResponse), typeof(GetNeighbourResponseObserver), "OnPeerNeighbourResponse")] + public void Can_Merge_PeerClientObservable_Stream_And_Read_Items_Pushed_On_Separate_Streams(Type discoveryMessage, + Type observer, + string logMsg) { var discoveryTestBuilder = new DiscoveryTestBuilder(); var subSeedOriginator = DiscoveryHelper.SubSeedOriginator(_ownNode, _settings); @@ -369,14 +369,12 @@ public void Can_Merge_PeerClientObservable_Stream_And_Read_Items_Pushed_On_Separ using (walker.DiscoveryStream.Subscribe(streamObserver.OnNext)) { - var subbedDto = DiscoveryHelper.SubDto(discoveryMessage, walker.StepProposal.PnrCorrelationId, walker.StepProposal.Peer); + var subbedDto = DiscoveryHelper.SubDto(discoveryMessage, walker.StepProposal.PnrCorrelationId, + walker.StepProposal.Peer); discoveryTestBuilder.PeerClientObservables .ToList() - .ForEach(o => - { - o.ResponseMessageSubject.OnNext(subbedDto); - }); + .ForEach(o => { o.ResponseMessageSubject.OnNext(subbedDto); }); _testScheduler.Start(); @@ -385,7 +383,7 @@ public void Can_Merge_PeerClientObservable_Stream_And_Read_Items_Pushed_On_Separ } } - [Fact] + [Test] public void Can_Discard_UnKnown_PingResponse() { var discoveryTestBuilder = new DiscoveryTestBuilder() @@ -401,7 +399,7 @@ public void Can_Discard_UnKnown_PingResponse() .WithBurn() .WithCareTaker() .WithStepProposal(default, false, _ownNode, default, CorrelationId.GenerateCorrelationId()); - + using (var walker = discoveryTestBuilder.Build()) { var streamObserver = Substitute.For>(); @@ -409,26 +407,27 @@ public void Can_Discard_UnKnown_PingResponse() using (walker.DiscoveryStream.Subscribe(streamObserver.OnNext)) { var subbedDto1 = Substitute.For(); - subbedDto1.Sender.Returns(PeerIdHelper.GetPeerId()); + subbedDto1.Sender.Returns(MultiAddressHelper.GetAddress()); subbedDto1.CorrelationId.Returns(Substitute.For()); subbedDto1.Message.Returns(new PingResponse()); - discoveryTestBuilder.PeerClientObservables.ToList().ForEach(o => o.ResponseMessageSubject.OnNext(subbedDto1)); + discoveryTestBuilder.PeerClientObservables.ToList() + .ForEach(o => o.ResponseMessageSubject.OnNext(subbedDto1)); _testScheduler.Start(); streamObserver.Received(1).OnNext(Arg.Any()); - + walker.StepProposal.Neighbours.Count.Should().Be(0); } } } - [Fact] + [Test] public void Unknown_Pnr_Message_Does_Not_Walk_Back() { - var candidatePid = PeerIdHelper.GetPeerId("candidate"); - var currentPid = PeerIdHelper.GetPeerId("current"); + var candidatePid = MultiAddressHelper.GetAddress("candidate"); + var currentPid = MultiAddressHelper.GetAddress("current"); var discoveryTestBuilder = new DiscoveryTestBuilder() .WithLogger() @@ -458,14 +457,17 @@ public void Unknown_Pnr_Message_Does_Not_Walk_Back() .Be(candidatePid); } } - - [Fact] + + [Test] public void Evicted_Known_Pnr_Message_Does_Walk_Back() { - var currentPid = PeerIdHelper.GetPeerId("current"); - var lastPid = PeerIdHelper.GetPeerId("last"); + var currentPid = MultiAddressHelper.GetAddress("current"); + var lastPid = MultiAddressHelper.GetAddress("last"); var mockNeighbours = DiscoveryHelper.MockNeighbours(4, NeighbourStateTypes.Responsive) - .Concat(new[] {new Neighbour(currentPid, NeighbourStateTypes.Contacted, CorrelationId.GenerateEmptyCorrelationId())}); + .Concat(new[] + { + new Neighbour(currentPid, NeighbourStateTypes.Contacted, CorrelationId.GenerateEmptyCorrelationId()) + }); var previousState = DiscoveryHelper.SubMemento(lastPid, new Neighbours(mockNeighbours)); var history = new Stack(); history.Push(previousState); @@ -493,13 +495,13 @@ public void Evicted_Known_Pnr_Message_Does_Walk_Back() .Be(lastPid); previousState.Neighbours - .Select(n => n.PeerId) + .Select(n => n.Address) .Should() .Contain(walker.StepProposal.Peer); } } - [Fact] + [Test] public void Can_Discard_UnKnown_PeerNeighbourResponse_Message() { var discoveryTestBuilder = new DiscoveryTestBuilder() @@ -515,7 +517,7 @@ public void Can_Discard_UnKnown_PeerNeighbourResponse_Message() .WithBurn() .WithCurrentStep() .WithStepProposal(default, false, _ownNode, default, CorrelationId.GenerateCorrelationId()); - + using (var walker = discoveryTestBuilder.Build()) { var streamObserver = Substitute.For>(); @@ -528,17 +530,14 @@ public void Can_Discard_UnKnown_PeerNeighbourResponse_Message() discoveryTestBuilder.PeerClientObservables .ToList() - .ForEach(o => - { - o.ResponseMessageSubject.OnNext(subbedDto); - }); + .ForEach(o => { o.ResponseMessageSubject.OnNext(subbedDto); }); walker.StepProposal.Neighbours.Count.Should().Be(0); } } } - [Fact] + [Test] public void Can_Process_Valid_PeerNeighbourResponse_Message_And_Ping_Provided_Neighbours() { var discoveryTestBuilder = new DiscoveryTestBuilder() @@ -554,7 +553,7 @@ public void Can_Process_Valid_PeerNeighbourResponse_Message_And_Ping_Provided_Ne .WithBurn() .WithCareTaker() .WithStepProposal(default, true, _ownNode, DiscoveryHelper.MockNeighbours()); - + using (var walker = discoveryTestBuilder.Build()) { var streamObserver = Substitute.For>(); @@ -573,8 +572,8 @@ public void Can_Process_Valid_PeerNeighbourResponse_Message_And_Ping_Provided_Ne walker.PeerClient .Received(Constants.NumberOfRandomPeers) - .SendMessage(Arg.Any>()); - + .SendMessageAsync(Arg.Any(), Arg.Any()); + walker.StepProposal.Neighbours .Where(n => n.StateTypes == NeighbourStateTypes.Contacted) .ToList().Count @@ -584,7 +583,7 @@ public void Can_Process_Valid_PeerNeighbourResponse_Message_And_Ping_Provided_Ne } } - private IPeerClientMessageDto GetPeerNeighbourResponse(DiscoveryTestBuilder.HastingDiscoveryTest walker, + private IPeerClientMessageDto GetPeerNeighbourResponse(DiscoveryTestBuilder.HastingDiscoveryTest walker, INeighbours neighbours) { var subbedDto = DiscoveryHelper.SubDto( @@ -595,17 +594,18 @@ private IPeerClientMessageDto GetPeerNeighbourResponse(DiscoveryTestBuilder.Hast var peerNeighborsResponse = new PeerNeighborsResponse(); peerNeighborsResponse.Peers.Add(neighbours - .Select(i => i.PeerId) + .Select(i => i.Address.ToString()) ); subbedDto.Message.Returns(peerNeighborsResponse); return subbedDto; } - [Fact] + [Test] public void Can_Correlate_Known_Ping_And_Update_Neighbour_State() { - var neighbours = DiscoveryHelper.MockNeighbours(Constants.NumberOfRandomPeers, NeighbourStateTypes.Contacted, CorrelationId.GenerateCorrelationId()); + var neighbours = DiscoveryHelper.MockNeighbours(Constants.NumberOfRandomPeers, + NeighbourStateTypes.Contacted, CorrelationId.GenerateCorrelationId()); var discoveryTestBuilder = new DiscoveryTestBuilder() .WithLogger() @@ -620,7 +620,7 @@ public void Can_Correlate_Known_Ping_And_Update_Neighbour_State() .WithBurn() .WithCurrentStep() .WithStepProposal(default, false, _ownNode, neighbours, CorrelationId.GenerateCorrelationId()); - + using (var walker = discoveryTestBuilder.Build()) { var streamObserver = Substitute.For>(); @@ -631,10 +631,11 @@ public void Can_Correlate_Known_Ping_And_Update_Neighbour_State() { neighbours.AsParallel().ForAll(n => { - var subbedDto = DiscoveryHelper.SubDto(typeof(PingResponse), n.DiscoveryPingCorrelationId, n.PeerId); + var subbedDto = DiscoveryHelper.SubDto(typeof(PingResponse), n.DiscoveryPingCorrelationId, + n.Address); var peerNeighborsResponse = new PingResponse(); - subbedDto.Message.Returns(peerNeighborsResponse); - + subbedDto.Message.Returns(peerNeighborsResponse); + discoveryTestBuilder.PeerClientObservables.ToList().ForEach(o => { o.ResponseMessageSubject.OnNext(subbedDto); @@ -643,18 +644,19 @@ public void Can_Correlate_Known_Ping_And_Update_Neighbour_State() _testScheduler.Start(); - walker.StepProposal.Neighbours.Count(n => n.StateTypes == NeighbourStateTypes.Responsive).Should().Be(neighbours.Count); + walker.StepProposal.Neighbours.Count(n => n.StateTypes == NeighbourStateTypes.Responsive).Should() + .Be(neighbours.Count); } } } - [Fact] - public void Known_Evicted_Correlation_Cache_PingRequest_Message_Increments_UnResponsivePeer() + [Test] + public void Known_Evicted_Correlation_Cache_PingRequest_Message_Increments_UnResponsivePeer() { var pnr = CorrelationId.GenerateCorrelationId(); - var unresponsiveNeighbour = new Neighbour(new PeerId(), NeighbourStateTypes.NotContacted, pnr); - - var initialMemento = DiscoveryHelper.SubMemento(_ownNode, + var unresponsiveNeighbour = new Neighbour(MultiAddressHelper.GetAddress(), NeighbourStateTypes.NotContacted, pnr); + + var initialMemento = DiscoveryHelper.SubMemento(_ownNode, DiscoveryHelper.MockDnsClient(_settings) .GetSeedNodesFromDnsAsync(_settings.SeedServers) .ConfigureAwait(false) @@ -662,11 +664,11 @@ public void Known_Evicted_Correlation_Cache_PingRequest_Message_Increments_UnRes .GetResult() .ToNeighbours() ); - + var initialStateCandidate = Substitute.For(); initialStateCandidate.Peer.Returns(initialMemento.Peer); initialStateCandidate.Neighbours.Returns(new Neighbours(new INeighbour[] {unresponsiveNeighbour})); - + initialStateCandidate.CreateMemento().Returns(initialMemento); initialStateCandidate.PnrCorrelationId.ReturnsForAnyArgs(CorrelationId.GenerateCorrelationId()); @@ -685,12 +687,13 @@ public void Known_Evicted_Correlation_Cache_PingRequest_Message_Increments_UnRes .WithBurn() .WithCurrentStep(initialMemento) .WithStepProposal(initialStateCandidate); - + using (var walker = discoveryTestBuilder.Build()) { walker.TestEvictionCallback(pnr); walker.StepProposal.Neighbours - .Count(n => n.StateTypes == NeighbourStateTypes.UnResponsive && n.DiscoveryPingCorrelationId.Equals(pnr)) + .Count(n => n.StateTypes == NeighbourStateTypes.UnResponsive && + n.DiscoveryPingCorrelationId.Equals(pnr)) .Should().Be(1); } } diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsMementoTests.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsMementoTests.cs index 42d9a7f5b2..ca1f2f7da1 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsMementoTests.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsMementoTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,25 +25,23 @@ using Catalyst.Protocol.Peer; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; +using MultiFormats; +using NUnit.Framework; namespace Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.UnitTests { public sealed class HastingsMementoTests { - private readonly PeerId _peer; + private readonly MultiAddress _peer; - public HastingsMementoTests() - { - _peer = PeerIdHelper.GetPeerId("current_peer"); - } - - [Fact] + public HastingsMementoTests() { _peer = MultiAddressHelper.GetAddress("current_peer"); } + + [Test] public void Can_Init_Memento_With_Existing_Params() { var neighbours = DiscoveryHelper.MockNeighbours(); var memento = new HastingsMemento(_peer, neighbours); - + memento.Peer.Should().Be(_peer); memento.Neighbours.Should().Contain(neighbours); memento.Neighbours.Should().HaveCount(Constants.NumberOfRandomPeers); diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsOriginatorTests.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsOriginatorTests.cs index 9da45104e7..4b26793afd 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsOriginatorTests.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests/UnitTests/HastingsOriginatorTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,20 +24,18 @@ using Catalyst.Protocol.Peer; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; +using MultiFormats; +using NUnit.Framework; namespace Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.UnitTests { public sealed class HastingsOriginatorTests { - private readonly PeerId _peer; + private readonly MultiAddress _peer; - public HastingsOriginatorTests() - { - _peer = PeerIdHelper.GetPeerId("current_peer"); - } + public HastingsOriginatorTests() { _peer = MultiAddressHelper.GetAddress("current_peer"); } - [Fact] + [Test] public void Can_Create_Memento_From_Current_State() { var memento = DiscoveryHelper.SubMemento(_peer); @@ -46,18 +44,18 @@ public void Can_Create_Memento_From_Current_State() var stateMemento = originator.CreateMemento(); stateMemento.Peer.Should().Be(_peer); - + stateMemento.Neighbours .Should() .BeEquivalentTo(memento.Neighbours); } - [Fact] + [Test] public void Can_Restore_State_From_Memento_And_Assign_New_CorrelationId() { var memento = DiscoveryHelper.MockMemento(); var originator = new HastingsOriginator(memento); - + originator.RestoreMemento(memento); originator.Peer.Should().Be(memento.Peer); @@ -66,19 +64,19 @@ public void Can_Restore_State_From_Memento_And_Assign_New_CorrelationId() originator.PnrCorrelationId.Should().NotBe(default); } - [Fact] + [Test] public void Can_Clean_Up_When_Calling_RestoreMemento() { var originator = HastingsOriginator.Default; var memento1 = DiscoveryHelper.SubMemento(); var memento2 = DiscoveryHelper.SubMemento(); - + originator.RestoreMemento(memento1); originator.RestoreMemento(memento2); originator.PnrCorrelationId.Should().NotBe(default); - + originator.Peer.Should().BeEquivalentTo(memento2.Peer); originator.Neighbours.Should().BeEquivalentTo(memento2.Neighbours); } diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/Catalyst.Core.Modules.P2P.Discovery.Hastings.csproj b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/Catalyst.Core.Modules.P2P.Discovery.Hastings.csproj index 6161f007a5..ba59d5cd48 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/Catalyst.Core.Modules.P2P.Discovery.Hastings.csproj +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/Catalyst.Core.Modules.P2P.Discovery.Hastings.csproj @@ -1,12 +1,15 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.P2P.Discovery.Hastings - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.P2P.Discovery.Hastings.snk true + + 1701;1702;CS8002 + diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/DiscoveryHastingModule.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/DiscoveryHastingModule.cs index 88d336f7f4..ed367ea1ba 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/DiscoveryHastingModule.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/DiscoveryHastingModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsCareTaker.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsCareTaker.cs index e5bfa8c82c..61ff8436df 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsCareTaker.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsCareTaker.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsDiscovery.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsDiscovery.cs index 559d824f55..70f259e7bb 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsDiscovery.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsDiscovery.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -31,18 +31,17 @@ using Catalyst.Abstractions.Network; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.P2P.Discovery; -using Catalyst.Abstractions.P2P.IO; using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; using Catalyst.Abstractions.P2P.IO.Messaging.Dto; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Abstractions.Types; using Catalyst.Abstractions.Util; +using Catalyst.Core.Lib.Abstractions.P2P.IO; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Core.Lib.P2P.Discovery; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.P2P.Discovery.Hastings @@ -50,14 +49,14 @@ namespace Catalyst.Core.Modules.P2P.Discovery.Hastings public class HastingsDiscovery : IPeerDiscovery, IDisposable { - private static readonly SemaphoreSlim SemaphoreSlim = new SemaphoreSlim(1, 1); + private static readonly SemaphoreSlim SemaphoreSlim = new(1, 1); private readonly ICancellationTokenProvider _cancellationTokenProvider; private readonly IDisposable _evictionSubscription; private readonly int _hasValidCandidatesCheckMillisecondsFrequency; private readonly ILogger _logger; private readonly int _millisecondsTimeout; private readonly IDisposable _neigbourResponseSubscription; - private readonly PeerId _ownNode; + private readonly MultiAddress _ownNode; private readonly IDisposable _pingResponseSubscriptions; protected readonly int PeerDiscoveryBurnIn; public readonly IPeerRepository PeerRepository; @@ -92,8 +91,7 @@ public HastingsDiscovery(ILogger logger, _discoveredPeerInCurrentWalk = 0; // build the initial step proposal for the walk, which is our node and seed nodes - _ownNode = peerSettings.PublicKey.BuildPeerIdFromBase32Key(peerSettings.BindAddress, - peerSettings.Port); + _ownNode = peerSettings.Address; var neighbours = dns.GetSeedNodesFromDnsAsync(peerSettings.SeedServers) .ConfigureAwait(false) @@ -270,15 +268,12 @@ protected void WalkForward() // continue walk by proposing next degree. var newCandidate = CurrentStep.Neighbours .Where(n => n.StateTypes == NeighbourStateTypes.Responsive) - .RandomElement().PeerId; + .RandomElement().Address; StepProposal.RestoreMemento(new HastingsMemento(newCandidate, new Neighbours())); - var peerNeighbourRequestDto = new MessageDto(new PeerNeighborsRequest().ToProtocolMessage(_ownNode), - StepProposal.Peer - ); - - PeerClient.SendMessage(peerNeighbourRequestDto); + var peerNeighbourRequest = new PeerNeighborsRequest().ToProtocolMessage(_ownNode); + PeerClient.SendMessageAsync(peerNeighbourRequest, StepProposal.Peer); } /// @@ -292,7 +287,7 @@ protected void WalkBack() do { responsiveNeighbours = CurrentStep.Neighbours - .Where(n => !n.PeerId.Equals(unresponsiveNeighbour) + .Where(n => n.Address != unresponsiveNeighbour && n.StateTypes == NeighbourStateTypes.Responsive) .ToList(); @@ -310,15 +305,12 @@ protected void WalkBack() "Peer discovery walked failed reaching its starting point with no responsive neighbours."); } - var newCandidate = responsiveNeighbours.RandomElement().PeerId; + var newCandidate = responsiveNeighbours.RandomElement().Address; StepProposal.RestoreMemento(new HastingsMemento(newCandidate, new Neighbours())); - var peerNeighbourRequestDto = new MessageDto(new PeerNeighborsRequest().ToProtocolMessage(_ownNode, StepProposal.PnrCorrelationId), - StepProposal.Peer - ); - - PeerClient.SendMessage(peerNeighbourRequestDto); + var peerNeighbourRequest = new PeerNeighborsRequest().ToProtocolMessage(_ownNode, StepProposal.PnrCorrelationId); + PeerClient.SendMessageAsync(peerNeighbourRequest, StepProposal.Peer); } /// @@ -335,7 +327,7 @@ protected void EvictionCallback(ICorrelationId requestCorrelationId) if (StepProposal.PnrCorrelationId.Equals(requestCorrelationId)) { // state candidate didn't give any neighbours so go back a step. - _logger.Verbose("StepProposal {n.PeerId} unresponsive."); + _logger.Verbose("StepProposal {n.Address} unresponsive."); WalkBack(); } @@ -350,7 +342,7 @@ protected void EvictionCallback(ICorrelationId requestCorrelationId) return; } - _logger.Verbose("Neighbour {peerId} unresponsive.", neighbour.PeerId); + _logger.Verbose("Neighbour {peerId} unresponsive.", neighbour.Address); neighbour.StateTypes = NeighbourStateTypes.UnResponsive; } catch (Exception e) @@ -380,7 +372,7 @@ private void OnPingResponse(IPeerClientMessageDto obj) return; } - StepProposal.Neighbours.First(n => n.PeerId.Equals(obj.Sender)).StateTypes = + StepProposal.Neighbours.First(n => n.Address.Equals(obj.Sender)).StateTypes = NeighbourStateTypes.Responsive; } catch (Exception e) @@ -416,10 +408,8 @@ private void OnPeerNeighbourResponse(IPeerClientMessageDto obj) { try { - var pingRequestDto = new MessageDto(new PingRequest().ToProtocolMessage(_ownNode, n.DiscoveryPingCorrelationId), - n.PeerId); - - PeerClient.SendMessage(pingRequestDto); + var pingRequest = new PingRequest().ToProtocolMessage(_ownNode, n.DiscoveryPingCorrelationId); + PeerClient.SendMessageAsync(pingRequest, n.Address); // our total expected responses should be same as number of pings sent out, // potential neighbours, can either send response, or we will see them evicted from cache. @@ -433,7 +423,7 @@ private void OnPeerNeighbourResponse(IPeerClientMessageDto obj) } }); - var newValidState = new HastingsMemento(StepProposal.Peer, new Neighbours(newNeighbours)); + HastingsMemento newValidState = new(StepProposal.Peer, new Neighbours(newNeighbours)); StepProposal.RestoreMemento(newValidState); } @@ -484,7 +474,7 @@ protected void StorePeer(INeighbour neighbour) { Reputation = 0, LastSeen = DateTime.UtcNow, - PeerId = neighbour.PeerId + Address = neighbour.Address }); Interlocked.Add(ref _discoveredPeerInCurrentWalk, 1); diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsMemento.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsMemento.cs index aa46da79f7..15d8a59555 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsMemento.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsMemento.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,6 +23,7 @@ using Catalyst.Abstractions.P2P.Discovery; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Core.Modules.P2P.Discovery.Hastings { @@ -31,10 +32,10 @@ namespace Catalyst.Core.Modules.P2P.Discovery.Hastings /// public sealed class HastingsMemento : IHastingsMemento { - public PeerId Peer { get; } + public MultiAddress Peer { get; } public INeighbours Neighbours { get; } - public HastingsMemento(PeerId peer, INeighbours neighbours) + public HastingsMemento(MultiAddress peer, INeighbours neighbours) { Peer = peer; Neighbours = neighbours; diff --git a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsOriginator.cs b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsOriginator.cs index b3f0a515d4..5c725b973a 100644 --- a/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsOriginator.cs +++ b/src/Catalyst.Core.Modules.P2P.Discovery.Hastings/HastingsOriginator.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,6 +30,7 @@ using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Core.Lib.P2P.Discovery; using Catalyst.Protocol.Peer; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.P2P.Discovery.Hastings @@ -41,9 +42,9 @@ public sealed class HastingsOriginator : IHastingsOriginator public INeighbours Neighbours { get; private set; } public ICorrelationId PnrCorrelationId { get; private set; } - public PeerId Peer { get; private set; } + public MultiAddress Peer { get; private set; } - public static readonly HastingsOriginator Default = new HastingsOriginator(default); + public static readonly HastingsOriginator Default = new(default); public HastingsOriginator(IHastingsMemento hastingsMemento) { diff --git a/src/Catalyst.Core.Modules.Rpc.Client.Tests/Catalyst.Core.Modules.Rpc.Client.Tests.csproj b/src/Catalyst.Core.Modules.Rpc.Client.Tests/Catalyst.Core.Modules.Rpc.Client.Tests.csproj index 78e339f6bf..af8b77b5f2 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client.Tests/Catalyst.Core.Modules.Rpc.Client.Tests.csproj +++ b/src/Catalyst.Core.Modules.Rpc.Client.Tests/Catalyst.Core.Modules.Rpc.Client.Tests.csproj @@ -1,18 +1,33 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Rpc.Client.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Rpc.Client.Tests.snk true + + + 1701;1702;VSTHRD200;CS8002 + - + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/AddFileToDfsResponseObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/AddFileToDfsResponseObserverTests.cs similarity index 87% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/AddFileToDfsResponseObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/AddFileToDfsResponseObserverTests.cs index 57c14479e6..b541be9b79 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/AddFileToDfsResponseObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/AddFileToDfsResponseObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,6 @@ #endregion using System.Threading; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.IO.Messaging.Correlation; @@ -33,9 +32,10 @@ using Google.Protobuf; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Client.Tests.UnitTests.IO.Observers { public sealed class AddFileToDfsResponseObserverTests { @@ -46,11 +46,11 @@ public AddFileToDfsResponseObserverTests() private readonly IUploadFileTransferFactory _uploadFileTransferFactory; - [Fact] + [Test] public void HandlerRemovesFileTransferOnError() { var channelHandlerContext = Substitute.For(); - var senderPeerIdentifier = PeerIdHelper.GetPeerId(); + var senderAddress = MultiAddressHelper.GetAddress(); var correlationId = CorrelationId.GenerateCorrelationId(); var addFileResponse = new AddFileToDfsResponse @@ -64,15 +64,15 @@ public void HandlerRemovesFileTransferOnError() _uploadFileTransferFactory ); addFileToDfsResponseObserver.HandleResponseObserver(addFileResponse, channelHandlerContext, - senderPeerIdentifier, correlationId); + senderAddress, correlationId); _uploadFileTransferFactory.Received(1).Remove(Arg.Any(), true); } - [Fact] + [Test] public void InitializesFileTransferOnSuccessResponse() { var channelHandlerContext = Substitute.For(); - var senderPeerId = PeerIdHelper.GetPeerId(); + var senderAddress = MultiAddressHelper.GetAddress(); var correlationId = CorrelationId.GenerateCorrelationId(); var addFileResponse = new AddFileToDfsResponse @@ -87,7 +87,7 @@ public void InitializesFileTransferOnSuccessResponse() ); addFileToDfsResponseObserver.HandleResponseObserver(addFileResponse, channelHandlerContext, - senderPeerId, correlationId); + senderAddress, correlationId); _uploadFileTransferFactory.Received(1) ?.FileTransferAsync(Arg.Any(), Arg.Any()); } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetFileFromDfsResponseObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/GetFileFromDfsResponseObserverTests.cs similarity index 88% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetFileFromDfsResponseObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/GetFileFromDfsResponseObserverTests.cs index 81c313c46d..719afc2381 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetFileFromDfsResponseObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/GetFileFromDfsResponseObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,6 @@ #endregion using System.Threading; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Extensions; @@ -35,18 +34,20 @@ using Google.Protobuf; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Client.Tests.UnitTests.IO.Observers { public sealed class GetFileFromDfsResponseObserverTests { - private readonly ILogger _logger; - private readonly IChannelHandlerContext _fakeContext; - private readonly IDownloadFileTransferFactory _fileDownloadFactory; - private readonly ulong ExpectedFileSize = 10; + private ILogger _logger; + private IChannelHandlerContext _fakeContext; + private IDownloadFileTransferFactory _fileDownloadFactory; + private ulong ExpectedFileSize = 10; - public GetFileFromDfsResponseObserverTests() + [SetUp] + public void Init() { _logger = Substitute.For(); _fakeContext = Substitute.For(); @@ -54,14 +55,14 @@ public GetFileFromDfsResponseObserverTests() _logger = Substitute.For(); } - [Fact] + [Test] public void Can_Expire_Download_File_Transfer_On_Error() { var correlationId = SendResponseToHandler(FileTransferResponseCodeTypes.Error); _fileDownloadFactory.GetFileTransferInformation(correlationId).Received(1).Expire(); } - [Fact] + [Test] public void Can_Start_File_Download_On_Successful_Response() { var correlationId = SendResponseToHandler(FileTransferResponseCodeTypes.Successful); @@ -69,7 +70,7 @@ public void Can_Start_File_Download_On_Successful_Response() _fileDownloadFactory.Received(1)?.FileTransferAsync(correlationId, CancellationToken.None); } - [Fact] + [Test] public void Does_Nothing_If_File_Transfer_Does_Not_Exist() { var responseCode = FileTransferResponseCodeTypes.Successful; @@ -110,7 +111,7 @@ private ProtocolMessage GetResponseMessage(ICorrelationId correlationId, FileTra { FileSize = ExpectedFileSize, ResponseCode = ByteString.CopyFrom((byte) responseCodeTypes.Id) - }.ToProtocolMessage(PeerIdHelper.GetPeerId("Test"), correlationId); + }.ToProtocolMessage(MultiAddressHelper.GetAddress("Test"), correlationId); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetMempoolResponseObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/GetMempoolResponseObserverTests.cs similarity index 88% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetMempoolResponseObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/GetMempoolResponseObserverTests.cs index 48d26b02b1..7fc63b7101 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetMempoolResponseObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/GetMempoolResponseObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,17 +28,17 @@ using DotNetty.Transport.Channels; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Client.Tests.UnitTests.IO.Observers { public sealed class GetMempoolResponseObserverTests { - [Fact] + [Test] public void Null_Mempool_Throws_Exception() { var channelHandlerContext = Substitute.For(); - var senderPeerIdentifier = PeerIdHelper.GetPeerId(); + var senderPeerIdentifier = MultiAddressHelper.GetAddress(); var correlationId = CorrelationId.GenerateCorrelationId(); var logger = Substitute.For(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetVersionResponseObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/GetVersionResponseObserverTests.cs similarity index 80% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetVersionResponseObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/GetVersionResponseObserverTests.cs index e57b0788e0..78baadd0af 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetVersionResponseObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/GetVersionResponseObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,29 +25,29 @@ using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Core.Modules.Rpc.Client.IO.Observers; using Catalyst.Protocol.Rpc.Node; -using Catalyst.TestUtils; using DotNetty.Transport.Channels; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.TestUtils; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Client.Tests.UnitTests.IO.Observers { public sealed class GetVersionResponseObserverTests { - [Fact] + [Test] public void Null_Version_Throws_Exception() { var channelHandlerContext = Substitute.For(); - var senderPeerIdentifier = PeerIdHelper.GetPeerId(); + var senderAddress = MultiAddressHelper.GetAddress(); var correlationId = CorrelationId.GenerateCorrelationId(); var logger = Substitute.For(); var getVersionResponseObserver = new GetVersionResponseObserver(logger); Assert.Throws(() => getVersionResponseObserver - .HandleResponseObserver(new VersionResponse {Version = null}, channelHandlerContext, - senderPeerIdentifier, correlationId)); + .HandleResponseObserver(new VersionResponse { Version = null }, channelHandlerContext, + senderAddress, correlationId)); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/RemovePeerResponseObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/RemovePeerResponseObserverTests.cs similarity index 88% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/RemovePeerResponseObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/RemovePeerResponseObserverTests.cs index e1afa278c3..9704d9dcf4 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/RemovePeerResponseObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/RemovePeerResponseObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Client.Tests.UnitTests.IO.Observers { public sealed class RemovePeerResponseObserverTests { } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/SignMessageResponseObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/SignMessageResponseObserverTests.cs similarity index 87% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/SignMessageResponseObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/SignMessageResponseObserverTests.cs index 532ed03f63..957ae284b0 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/SignMessageResponseObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/IO/Observers/SignMessageResponseObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,20 +32,20 @@ using DotNetty.Transport.Channels; using Google.Protobuf; using Microsoft.Reactive.Testing; +using MultiFormats; using NSubstitute; +using NUnit.Framework; using Serilog; -using TheDotNetLeague.MultiFormats.MultiBase; -using Xunit; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Client.Tests.UnitTests.IO.Observers { public sealed class SignMessageResponseHandlerTests : IDisposable { - private readonly ILogger _logger; - private readonly IChannelHandlerContext _fakeContext; + private ILogger _logger; + private IChannelHandlerContext _fakeContext; public static readonly List QueryContents = InitialiseQueryData(); - private readonly IUserOutput _output; + private IUserOutput _output; private SignMessageResponseObserver _handler; @@ -63,7 +63,7 @@ public bool Equals(SignedResponse other) } } - static List InitialiseQueryData() + private static List InitialiseQueryData() { return new List { @@ -78,7 +78,8 @@ static List InitialiseQueryData() }; } - public SignMessageResponseHandlerTests() + [SetUp] + public void Init() { _logger = Substitute.For(); _fakeContext = Substitute.For(); @@ -97,8 +98,7 @@ private static SignedResponse SignMessage(string messageToSign, string signature return signedResponse; } - [Theory] - [MemberData(nameof(QueryContents))] + [TestCaseSource(nameof(QueryContents))] public void RpcClient_Can_Handle_SignMessageResponse(SignedResponse signedResponse) { var testScheduler = new TestScheduler(); @@ -113,7 +113,7 @@ public void RpcClient_Can_Handle_SignMessageResponse(SignedResponse signedRespon var correlationId = CorrelationId.GenerateCorrelationId(); var protocolMessage = - signMessageResponse.ToProtocolMessage(PeerIdHelper.GetPeerId("sender"), correlationId); + signMessageResponse.ToProtocolMessage(MultiAddressHelper.GetAddress("sender"), correlationId); var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, protocolMessage diff --git a/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/NodeRpcClientFactoryTests.cs b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/NodeRpcClientFactoryTests.cs index 0ffaa99754..e468fa99d2 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/NodeRpcClientFactoryTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/NodeRpcClientFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,15 +22,17 @@ #endregion using System.Collections.Generic; -using System.Net; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.Rpc; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc; +using Catalyst.Protocol.Wire; using FluentAssertions; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Rpc.Client.Tests.UnitTests { @@ -38,7 +40,7 @@ public sealed class RpcClientFactoryTests { public RpcClientFactoryTests() { - var channelFactory = Substitute.For(); + var channelFactory = Substitute.For>>(); var clientEventLoopGroupFactory = Substitute.For(); _rpcClientFactory = new RpcClientFactory(channelFactory, clientEventLoopGroupFactory, new List()); @@ -46,15 +48,14 @@ public RpcClientFactoryTests() private readonly RpcClientFactory _rpcClientFactory; - [Fact] + [Test] public async Task GetClient_Should_Return_RpcClient() { var nodeRpcConfig = Substitute.For(); - nodeRpcConfig.HostAddress = IPAddress.Any; nodeRpcConfig.NodeId = "0"; nodeRpcConfig.PfxFileName = "pfx"; - nodeRpcConfig.Port = 9000; - var rpcClient = await _rpcClientFactory.GetClient(null, nodeRpcConfig); + nodeRpcConfig.Address = "/ip4/127.0.0.1/tcp/9000"; + var rpcClient = await _rpcClientFactory.GetClientAsync(null, nodeRpcConfig); rpcClient.Should().BeAssignableTo(); } diff --git a/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/NodeRpcClientTests.cs b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/NodeRpcClientTests.cs index 008e9cb202..cd0405fc61 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/NodeRpcClientTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client.Tests/UnitTests/NodeRpcClientTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,23 +22,15 @@ #endregion using System.Collections.Generic; -using System.Net; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.Rpc; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Core.Lib.IO.Transport.Channels; using Catalyst.Core.Lib.Rpc.IO.Exceptions; using Catalyst.Core.Modules.Rpc.Client.IO.Observers; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; using Catalyst.TestUtils; @@ -47,7 +39,14 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; namespace Catalyst.Core.Modules.Rpc.Client.Tests.UnitTests { @@ -57,34 +56,33 @@ public RpcClientTests() { _testScheduler = new TestScheduler(); _logger = Substitute.For(); - _peerIdentifier = PeerIdHelper.GetPeerId("Test"); + _peerIdentifier = MultiAddressHelper.GetAddress("Test"); _channelHandlerContext = Substitute.For(); - _channelFactory = Substitute.For(); + _channelFactory = Substitute.For>>(); _clientEventLoopGroupFactory = Substitute.For(); _mockSocketReplySubject = new ReplaySubject>(1, _testScheduler); var mockChannel = Substitute.For(); var mockEventStream = _mockSocketReplySubject.AsObservable(); - var observableChannel = new ObservableChannel(mockEventStream, mockChannel); + RpcObservableChannel observableChannel = new(mockEventStream, mockChannel); - _channelFactory.BuildChannelAsync(_clientEventLoopGroupFactory, Arg.Any(), Arg.Any(), + _channelFactory.BuildChannelAsync(_clientEventLoopGroupFactory, Arg.Any(), Arg.Any()).Returns(observableChannel); _rpcClientConfig = Substitute.For(); - _rpcClientConfig.HostAddress = IPAddress.Any; _rpcClientConfig.NodeId = "0"; _rpcClientConfig.PfxFileName = "pfx"; - _rpcClientConfig.Port = 9000; + _rpcClientConfig.Address = new MultiAddress("/ip4/127.0.0.1/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"); } private readonly ILogger _logger; private readonly TestScheduler _testScheduler; - private readonly PeerId _peerIdentifier; + private readonly MultiAddress _peerIdentifier; - private readonly ITcpClientChannelFactory _channelFactory; + private readonly ITcpClientChannelFactory> _channelFactory; private readonly ITcpClientEventLoopGroupFactory _clientEventLoopGroupFactory; @@ -94,17 +92,17 @@ public RpcClientTests() private readonly ReplaySubject> _mockSocketReplySubject; - [Fact] + [Test] public async Task SubscribeToResponse_Should_Not_Return_Invalid_Response() { - var nodeRpcClientFactory = new RpcClientFactory(_channelFactory, _clientEventLoopGroupFactory, - new List {new GetVersionResponseObserver(_logger)}); - var nodeRpcClient = await nodeRpcClientFactory.GetClient(null, _rpcClientConfig); + RpcClientFactory nodeRpcClientFactory = new(_channelFactory, _clientEventLoopGroupFactory, + new List { new GetVersionResponseObserver(_logger) }); + var nodeRpcClient = await nodeRpcClientFactory.GetClientAsync(null, _rpcClientConfig); var receivedResponse = false; - var targetVersionResponse = new VersionResponse {Version = "1.2.3.4"}; + var targetVersionResponse = new VersionResponse { Version = "1.2.3.4" }; var protocolMessage = targetVersionResponse.ToProtocolMessage(_peerIdentifier, CorrelationId.GenerateCorrelationId()); - var observerDto = new ObserverDto(_channelHandlerContext, protocolMessage); + ObserverDto observerDto = new(_channelHandlerContext, protocolMessage); _mockSocketReplySubject.OnNext(observerDto); nodeRpcClient.SubscribeToResponse(response => receivedResponse = true); @@ -113,17 +111,17 @@ public async Task SubscribeToResponse_Should_Not_Return_Invalid_Response() receivedResponse.Should().BeFalse(); } - [Fact] + [Test] public async Task SubscribeToResponse_Should_Return_Response() { - var nodeRpcClientFactory = new RpcClientFactory(_channelFactory, _clientEventLoopGroupFactory, - new List {new GetVersionResponseObserver(_logger)}); - var nodeRpcClient = await nodeRpcClientFactory.GetClient(null, _rpcClientConfig); + RpcClientFactory nodeRpcClientFactory = new(_channelFactory, _clientEventLoopGroupFactory, + new List { new GetVersionResponseObserver(_logger) }); + var nodeRpcClient = await nodeRpcClientFactory.GetClientAsync(null, _rpcClientConfig); VersionResponse returnedVersionResponse = null; - var targetVersionResponse = new VersionResponse {Version = "1.2.3.4"}; + var targetVersionResponse = new VersionResponse { Version = "1.2.3.4" }; var protocolMessage = targetVersionResponse.ToProtocolMessage(_peerIdentifier, CorrelationId.GenerateCorrelationId()); - var observerDto = new ObserverDto(_channelHandlerContext, protocolMessage); + ObserverDto observerDto = new(_channelHandlerContext, protocolMessage); _mockSocketReplySubject.OnNext(observerDto); nodeRpcClient.SubscribeToResponse(response => returnedVersionResponse = response); @@ -132,25 +130,23 @@ public async Task SubscribeToResponse_Should_Return_Response() targetVersionResponse.Should().Be(returnedVersionResponse); } - [Fact] + [Test] public async Task SubscribeToResponse_Without_Response_Handler_Should_Throw_Exception() { - var nodeRpcClientFactory = new RpcClientFactory(_channelFactory, _clientEventLoopGroupFactory, + RpcClientFactory nodeRpcClientFactory = new(_channelFactory, _clientEventLoopGroupFactory, new List()); - var nodeRpcClient = await nodeRpcClientFactory.GetClient(null, _rpcClientConfig); - var targetVersionResponse = new VersionResponse {Version = "1.2.3.4"}; + var nodeRpcClient = await nodeRpcClientFactory.GetClientAsync(null, _rpcClientConfig); + var targetVersionResponse = new VersionResponse { Version = "1.2.3.4" }; var protocolMessage = targetVersionResponse.ToProtocolMessage(_peerIdentifier, CorrelationId.GenerateCorrelationId()); - var observerDto = new ObserverDto(_channelHandlerContext, protocolMessage); + ObserverDto observerDto = new(_channelHandlerContext, protocolMessage); - var exception = Record.Exception(() => + Assert.Throws(() => { _mockSocketReplySubject.OnNext(observerDto); nodeRpcClient.SubscribeToResponse(response => { }); _testScheduler.Start(); }); - - exception.Should().BeOfType(); } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/Catalyst.Core.Modules.Rpc.Client.csproj b/src/Catalyst.Core.Modules.Rpc.Client/Catalyst.Core.Modules.Rpc.Client.csproj index 3e86477f4b..d1f8f24cd6 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/Catalyst.Core.Modules.Rpc.Client.csproj +++ b/src/Catalyst.Core.Modules.Rpc.Client/Catalyst.Core.Modules.Rpc.Client.csproj @@ -1,18 +1,22 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Rpc.Client - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Rpc.Client.snk true + + 1701;1702;CS8002 + + - + diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Messaging/Dto/RPCClientMessageDto.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Messaging/Dto/RPCClientMessageDto.cs index e28f92a301..5b3bdc94bd 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Messaging/Dto/RPCClientMessageDto.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Messaging/Dto/RPCClientMessageDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/AddFileToDfsResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/AddFileToDfsResponseObserver.cs index d3826c1df7..e6e8584923 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/AddFileToDfsResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/AddFileToDfsResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,15 +22,16 @@ #endregion using System.Threading; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; using Catalyst.Protocol.Rpc.Node; +using Catalyst.Protocol.Wire; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -58,16 +59,16 @@ public AddFileToDfsResponseObserver(ILogger logger, /// /// /// - /// + /// /// protected override void HandleResponse(AddFileToDfsResponse addFileToDfsResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderAddress, ICorrelationId correlationId) { Guard.Argument(addFileToDfsResponse, nameof(addFileToDfsResponse)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerIdentifier, nameof(senderPeerIdentifier)).NotNull(); + Guard.Argument(senderAddress, nameof(senderAddress)).NotNull(); // @TODO return int not byte // var responseCode = Enumeration.Parse(deserialised.ResponseCode[0].ToString()); diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/BroadcastRawTransactionResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/BroadcastRawTransactionResponseObserver.cs index d1b8bd6861..2cadc1fd2c 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/BroadcastRawTransactionResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/BroadcastRawTransactionResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,9 +23,9 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -34,9 +34,9 @@ public class BroadcastRawTransactionResponseObserver : RpcResponseObserver * @@ -22,11 +22,10 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -43,7 +42,7 @@ public ChangeDataFolderResponseObserver(ILogger logger) protected override void HandleResponse(SetPeerDataFolderResponse setPeerDataFolderResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderAddress, ICorrelationId correlationId) { //Overriden code diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetDeltaResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetDeltaResponseObserver.cs index 7e58f9d836..92dc519dea 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetDeltaResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetDeltaResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,12 +22,11 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Core.Lib.IO.Observers; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -41,7 +40,7 @@ public GetDeltaResponseObserver(ILogger logger) : base(logger) { } /// protected override void HandleResponse(GetDeltaResponse deltaResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderAddress, ICorrelationId correlationId) { } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetFileFromDfsResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetFileFromDfsResponseObserver.cs index 8b5c25d714..72321de431 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetFileFromDfsResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetFileFromDfsResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,15 +24,14 @@ using System.IO; using System.Threading; using System.Threading.Tasks; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -61,16 +60,16 @@ public GetFileFromDfsResponseObserver(ILogger logger, ///
/// /// - /// + /// /// protected override void HandleResponse(GetFileFromDfsResponse getFileFromDfsResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderAddress, ICorrelationId correlationId) { Guard.Argument(getFileFromDfsResponse, nameof(getFileFromDfsResponse)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerIdentifier, nameof(senderPeerIdentifier)).NotNull(); + Guard.Argument(senderAddress, nameof(senderAddress)).NotNull(); var responseCode = (FileTransferResponseCodeTypes) getFileFromDfsResponse.ResponseCode[0]; @@ -85,7 +84,7 @@ protected override void HandleResponse(GetFileFromDfsResponse getFileFromDfsResp { fileTransferInformation.SetLength(getFileFromDfsResponse.FileSize); - var ctx = new CancellationTokenSource(); + CancellationTokenSource ctx = new(); _fileTransferFactory.FileTransferAsync(fileTransferInformation.CorrelationId, CancellationToken.None)?.ContinueWith(task => { File.Move(fileTransferInformation.TempPath, fileTransferInformation.FileOutputPath); diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetInfoResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetInfoResponseObserver.cs index 2e07bbcbb0..01c71f409c 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetInfoResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetInfoResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,9 +23,9 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -47,11 +47,11 @@ public GetInfoResponseObserver(ILogger logger) /// /// /// - /// + /// /// protected override void HandleResponse(GetInfoResponse getInfoResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderAddress, ICorrelationId correlationId) { } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetMempoolResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetMempoolResponseObserver.cs index 1baa10f11a..2a5de4d0f7 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetMempoolResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetMempoolResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,10 +23,10 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -40,7 +40,7 @@ public sealed class GetMempoolResponseObserver { /// /// - /// A service used to output the result of the messages handling to the user. + /// A repository used to output the result of the messages handling to the user. /// output /// /// @@ -54,11 +54,11 @@ public GetMempoolResponseObserver(ILogger logger) /// /// /// - /// + /// /// protected override void HandleResponse(GetMempoolResponse getMempoolResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderAddress, ICorrelationId correlationId) { Guard.Argument(getMempoolResponse, nameof(getMempoolResponse)) diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetPeerInfoResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetPeerInfoResponseObserver.cs index 894820cee7..a5634c8d8e 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetPeerInfoResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetPeerInfoResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,11 +22,10 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -50,11 +49,11 @@ public GetPeerInfoResponseObserver(ILogger logger) /// /// The response /// The channel handler context - /// The sender peer identifier + /// The sender peer identifier /// The correlationId protected override void HandleResponse(GetPeerInfoResponse getPeerInfoResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderAddress, ICorrelationId correlationId) { } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetVersionResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetVersionResponseObserver.cs index 3a41639267..4f45f1e29a 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetVersionResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/GetVersionResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,10 +23,10 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -52,11 +52,11 @@ public GetVersionResponseObserver(ILogger logger) /// /// /// - /// + /// /// protected override void HandleResponse(VersionResponse versionResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderAddress, ICorrelationId correlationId) { Guard.Argument(versionResponse, nameof(versionResponse)).NotNull("The VersionResponse cannot be null") diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerBlackListingResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerBlackListingResponseObserver.cs index ca748445af..bc9bf9c329 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerBlackListingResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerBlackListingResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,11 +22,10 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -36,14 +35,14 @@ namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers /// /// public sealed class PeerBlackListingResponseObserver - : RpcResponseObserver + : RpcResponseObserver { public PeerBlackListingResponseObserver(ILogger logger) : base(logger) { } - protected override void HandleResponse(SetPeerBlacklistResponse setPeerBlackListResponse, + protected override void HandleResponse(SetPeerBlackListResponse setPeerBlackListResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderAddress, ICorrelationId correlationId) { } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerCountResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerCountResponseObserver.cs index 79a44a8b7b..f633b8a51e 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerCountResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerCountResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,9 +24,9 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.IO.Observers; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -43,7 +43,7 @@ public PeerCountResponseObserver(ILogger logger) protected override void HandleResponse(GetPeerCountResponse getPeerCountResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderentifier, ICorrelationId correlationId) { } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerListResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerListResponseObserver.cs index f391c3aaa8..abf6c3b443 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerListResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerListResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,11 +22,10 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -43,7 +42,7 @@ public PeerListResponseObserver(ILogger logger) protected override void HandleResponse(GetPeerListResponse getPeerListResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderAddress, ICorrelationId correlationId) { } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerReputationResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerReputationResponseObserver.cs index 31b23db735..d6ebbbc284 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerReputationResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/PeerReputationResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,11 +22,10 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -43,7 +42,7 @@ public PeerReputationResponseObserver(ILogger logger) protected override void HandleResponse(GetPeerReputationResponse getPeerReputationResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderentifier, ICorrelationId correlationId) { } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/RemovePeerResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/RemovePeerResponseObserver.cs index febaf5b7c5..2cca25be69 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/RemovePeerResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/RemovePeerResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,11 +22,10 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -42,7 +41,7 @@ public RemovePeerResponseObserver(ILogger logger) : base(logger) { } protected override void HandleResponse(RemovePeerResponse removePeerResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderentifier, ICorrelationId correlationId) { } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/SignMessageResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/SignMessageResponseObserver.cs index 23cf94f014..e7978bdcb3 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/SignMessageResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/SignMessageResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,12 +25,11 @@ using Catalyst.Abstractions.Cli; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; -using TheDotNetLeague.MultiFormats.MultiBase; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers { @@ -59,16 +58,16 @@ public SignMessageResponseObserver(IUserOutput output, /// /// /// - /// + /// /// protected override void HandleResponse(SignMessageResponse signMessageRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderentifier, ICorrelationId correlationId) { Guard.Argument(signMessageRequest, nameof(signMessageRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerIdentifier, nameof(senderPeerIdentifier)).NotNull(); + Guard.Argument(senderentifier, nameof(senderentifier)).NotNull(); Logger.Debug(@"sign message response"); try diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/TransferFileBytesResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/TransferFileBytesResponseObserver.cs index 0f790e9b28..7e84d1104b 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/TransferFileBytesResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/TransferFileBytesResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,12 +21,12 @@ #endregion -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -47,9 +47,9 @@ public TransferFileBytesResponseObserver(IDownloadFileTransferFactory fileTransf _fileTransferFactory = fileTransferFactory; } - protected override void HandleResponse(TransferFileBytesRequest message, - IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + protected override void HandleResponse(TransferFileBytesRequest message, + IChannelHandlerContext channelHandlerContext, + MultiAddress senderentifier, ICorrelationId correlationId) { _fileTransferFactory.DownloadChunk(message); diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/VerifyMessageResponseObserver.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/VerifyMessageResponseObserver.cs index a44ed84b80..9370c74f66 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/VerifyMessageResponseObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Observers/VerifyMessageResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,10 +23,10 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Client.IO.Observers @@ -50,16 +50,16 @@ public VerifyMessageResponseObserver(ILogger logger) /// /// /// - /// + /// /// protected override void HandleResponse(VerifyMessageResponse verifyMessageResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress senderentifier, ICorrelationId correlationId) { Guard.Argument(verifyMessageResponse, nameof(verifyMessageResponse)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerIdentifier, nameof(senderPeerIdentifier)).NotNull(); + Guard.Argument(senderentifier, nameof(senderentifier)).NotNull(); } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/IO/Transport/Channels/RpcClientChannelFactory.cs b/src/Catalyst.Core.Modules.Rpc.Client/IO/Transport/Channels/RpcClientChannelFactory.cs index 8eb0b8af4a..343134628e 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/IO/Transport/Channels/RpcClientChannelFactory.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/IO/Transport/Channels/RpcClientChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,35 +23,35 @@ using System; using System.Collections.Generic; -using System.Net; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; using Catalyst.Abstractions.IO.Handlers; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Rpc.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Codecs; -using Catalyst.Core.Lib.IO.Handlers; -using Catalyst.Core.Lib.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Codecs; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Wire; using DotNetty.Buffers; using DotNetty.Codecs.Protobuf; using DotNetty.Transport.Channels; +using MultiFormats; namespace Catalyst.Core.Modules.Rpc.Client.IO.Transport.Channels { - public class RpcClientChannelFactory : TcpClientChannelFactory + public class RpcClientChannelFactory : TcpClientChannelFactory> { private readonly IKeySigner _keySigner; private readonly IRpcMessageCorrelationManager _messageCorrelationCache; private readonly IPeerIdValidator _peerIdValidator; - private readonly IObservableServiceHandler _observableServiceHandler; + private readonly IObservableServiceHandler> _observableServiceHandler; private readonly SigningContext _signingContext; /// @@ -74,7 +74,7 @@ public RpcClientChannelFactory(IKeySigner keySigner, _messageCorrelationCache = messageCorrelationCache; _peerIdValidator = peerIdValidator; _signingContext = new SigningContext {NetworkType = peerSettings.NetworkType, SignatureType = SignatureType.ProtocolRpc}; - _observableServiceHandler = new ObservableServiceHandler(observableScheduler); + _observableServiceHandler = new RpcObservableServiceHandler(observableScheduler); } protected override Func> HandlerGenerationFunction @@ -105,17 +105,15 @@ protected override Func> HandlerGenerationFunction /// Ignored /// Ignored /// Local TLS certificate - public override async Task BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + public override async Task>> BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, + MultiAddress address, X509Certificate2 certificate = null) { - var channel = await BootstrapAsync(eventLoopGroupFactory, targetAddress, targetPort, certificate).ConfigureAwait(false); + var channel = await BootstrapAsync(eventLoopGroupFactory, address, certificate).ConfigureAwait(false); var messageStream = _observableServiceHandler.MessageStream; - return new ObservableChannel(messageStream - ?? Observable.Never>(), channel); + return new ObservableChannel>(messageStream ?? Observable.Never>(), channel); } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/RpcClient.cs b/src/Catalyst.Core.Modules.Rpc.Client/RpcClient.cs index ac81a67306..523df27643 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/RpcClient.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/RpcClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,14 +28,15 @@ using System.Reflection; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.Rpc; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Transport; using Catalyst.Core.Lib.Rpc.IO.Exceptions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Rpc; using Catalyst.Protocol.Wire; using Google.Protobuf; using Serilog; @@ -46,9 +47,9 @@ namespace Catalyst.Core.Modules.Rpc.Client /// This class provides a command line interface (CLI) application to connect to Catalyst Node. /// Through the CLI the node operator will be able to connect to any number of running nodes and run commands. /// - public sealed class RpcClient : TcpClient, IRpcClient + public sealed class RpcClient : TcpClient>, IRpcClient { - private readonly ITcpClientChannelFactory _channelFactory; + private readonly ITcpClientChannelFactory> _channelFactory; private readonly IEnumerable _rpcResponseObservers; private readonly X509Certificate2 _certificate; private readonly IRpcClientConfig _clientConfig; @@ -63,7 +64,7 @@ public sealed class RpcClient : TcpClient, IRpcClient /// rpc node config /// /// - public RpcClient(ITcpClientChannelFactory channelFactory, + public RpcClient(ITcpClientChannelFactory> channelFactory, X509Certificate2 certificate, IRpcClientConfig clientConfig, IEnumerable handlers, @@ -93,16 +94,14 @@ private T SubscriptionOutPipeline(IObserverDto observer) whe } var handler = _handlers[observer.Payload.TypeUrl]; - handler.HandleResponseObserver(message, observer.Context, observer.Payload.PeerId, - observer.Payload.CorrelationId.ToCorrelationId()); + handler.HandleResponseObserver(message, observer.Context, observer.Payload.Address, observer.Payload.CorrelationId.ToCorrelationId()); return message; } public override async Task StartAsync() { - var socket = await _channelFactory.BuildChannelAsync(EventLoopGroupFactory, _clientConfig.HostAddress, _clientConfig.Port, - _certificate).ConfigureAwait(false); + var socket = await _channelFactory.BuildChannelAsync(EventLoopGroupFactory, _clientConfig.Address, _certificate).ConfigureAwait(false); _socketMessageStream = socket.MessageStream; diff --git a/src/Catalyst.Core.Modules.Rpc.Client/RpcClientFactory.cs b/src/Catalyst.Core.Modules.Rpc.Client/RpcClientFactory.cs index 904ff68efb..3ac00185bb 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/RpcClientFactory.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/RpcClientFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,20 +24,23 @@ using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.Rpc; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc; +using Catalyst.Protocol.Wire; namespace Catalyst.Core.Modules.Rpc.Client { public sealed class RpcClientFactory : IRpcClientFactory { - private readonly ITcpClientChannelFactory _channelFactory; + private readonly ITcpClientChannelFactory> _channelFactory; private readonly IEnumerable _handlers; private readonly ITcpClientEventLoopGroupFactory _clientEventLoopGroupFactory; - public RpcClientFactory(ITcpClientChannelFactory channelFactory, + public RpcClientFactory(ITcpClientChannelFactory> channelFactory, ITcpClientEventLoopGroupFactory clientEventLoopGroupFactory, IEnumerable handlers) { @@ -46,9 +49,9 @@ public RpcClientFactory(ITcpClientChannelFactory channelFactory, _handlers = handlers; } - public async Task GetClient(X509Certificate2 certificate, IRpcClientConfig clientConfig) + public async Task GetClientAsync(X509Certificate2 certificate, IRpcClientConfig clientConfig) { - var nodeRpcClient = new RpcClient(_channelFactory, certificate, clientConfig, _handlers, _clientEventLoopGroupFactory); + RpcClient nodeRpcClient = new(_channelFactory, certificate, clientConfig, _handlers, _clientEventLoopGroupFactory); await nodeRpcClient.StartAsync(); return nodeRpcClient; diff --git a/src/Catalyst.Core.Modules.Rpc.Client/RpcClientModule.cs b/src/Catalyst.Core.Modules.Rpc.Client/RpcClientModule.cs index 10deba23d6..384f4b17ce 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/RpcClientModule.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/RpcClientModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,9 +22,12 @@ #endregion using Autofac; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.Rpc; using Catalyst.Core.Modules.Rpc.Client.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Rpc; +using Catalyst.Protocol.Wire; namespace Catalyst.Core.Modules.Rpc.Client { @@ -33,7 +36,7 @@ public class RpcClientModule : Module protected override void Load(ContainerBuilder builder) { builder.RegisterType().As(); - builder.RegisterType().As(); + builder.RegisterType().As>>(); builder.RegisterType().As(); builder.RegisterType().As(); } diff --git a/src/Catalyst.Core.Modules.Rpc.Client/RpcClientSettings.cs b/src/Catalyst.Core.Modules.Rpc.Client/RpcClientSettings.cs index dcd5e63542..31708e2688 100644 --- a/src/Catalyst.Core.Modules.Rpc.Client/RpcClientSettings.cs +++ b/src/Catalyst.Core.Modules.Rpc.Client/RpcClientSettings.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,9 +23,9 @@ using System.Collections.Generic; using System.Linq; -using System.Net; using Catalyst.Abstractions.Rpc; using Microsoft.Extensions.Configuration; +using MultiFormats; namespace Catalyst.Core.Modules.Rpc.Client { @@ -34,25 +34,21 @@ public sealed class RpcClientSettings : IRpcClientConfig public static IList BuildRpcNodeSettingList(IConfigurationRoot config) { var section = config.GetSection("CatalystCliRpcNodes").GetSection("nodes"); - + var children = section.GetChildren().Select(c=>c.GetChildren()); var nodeList = section.GetChildren().Select(child => new RpcClientSettings { NodeId = child.GetSection("nodeId").Value, - HostAddress = IPAddress.Parse(child.GetSection("host").Value), - Port = int.Parse(child.GetSection("port").Value), PfxFileName = child.GetSection("PfxFileName").Value, SslCertPassword = child.GetSection("SslCertPassword").Value, - PublicKey = child.GetSection("PublicKey").Value + Address = child.GetSection("Address").Value } as IRpcClientConfig).ToList(); return nodeList; } + public MultiAddress Address { get; set; } public string NodeId { get; set; } - public IPAddress HostAddress { get; set; } - public int Port { get; set; } public string PfxFileName { get; set; } public string SslCertPassword { get; set; } - public string PublicKey { get; set; } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Server.Tests/Catalyst.Core.Modules.Rpc.Server.Tests.csproj b/src/Catalyst.Core.Modules.Rpc.Server.Tests/Catalyst.Core.Modules.Rpc.Server.Tests.csproj index c2948c7597..f3bd781fb6 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server.Tests/Catalyst.Core.Modules.Rpc.Server.Tests.csproj +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/Catalyst.Core.Modules.Rpc.Server.Tests.csproj @@ -1,18 +1,31 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Rpc.Server.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Rpc.Server.Tests.snk true - + + 1701;1702;VSTHRD200;CS8002 + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/GetFileFromDfsObserverHandlerTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/GetFileFromDfsObserverHandlerTests.cs similarity index 70% rename from src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/GetFileFromDfsObserverHandlerTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/GetFileFromDfsObserverHandlerTests.cs index 10106681f3..1c41da5e05 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/GetFileFromDfsObserverHandlerTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/GetFileFromDfsObserverHandlerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,52 +25,55 @@ using System.IO; using System.Threading.Tasks; using Catalyst.Abstractions.Dfs; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.FileTransfer; using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Core.Modules.Hashing; -using Catalyst.Core.Modules.Rpc.Client.IO.Observers; -using Catalyst.Core.Modules.Rpc.Server.IO.Observers; using Catalyst.Protocol.Rpc.Node; using Catalyst.TestUtils; using DotNetty.Transport.Channels; using Google.Protobuf; +using MultiFormats; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.FileTransfer; +using Catalyst.Core.Modules.Rpc.Client.IO.Observers; +using Catalyst.Core.Modules.Rpc.Server.IO.Observers; -namespace Catalyst.Core.Lib.Tests.IntegrationTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.IntegrationTests.IO.Observers { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class GetFileFromDfsObserverHandlerTests : FileSystemBasedTest { - private readonly ILogger _logger; - private readonly IChannelHandlerContext _fakeContext; - private readonly IDownloadFileTransferFactory _fileDownloadFactory; - private readonly IDfs _dfs; - private readonly IHashProvider _hashProvider; + private ILogger _logger; + private IChannelHandlerContext _fakeContext; + private IDownloadFileTransferFactory _fileDownloadFactory; + private IDfsService _dfsService; + private IHashProvider _hashProvider; - public GetFileFromDfsObserverHandlerTests(ITestOutputHelper testOutput) : base(testOutput) + [SetUp] + public void Init() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + Setup(TestContext.CurrentContext); + + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); _logger = Substitute.For(); _fakeContext = Substitute.For(); _fileDownloadFactory = new DownloadFileTransferFactory(_logger); _logger = Substitute.For(); - _dfs = Substitute.For(); + _dfsService = Substitute.For(); } - [Theory] - [InlineData(1000L)] - [InlineData(82000L)] - [InlineData(100000L)] - [InlineData(800000L)] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [TestCase(1000L)] + [TestCase(82000L)] + [TestCase(100000L)] + [TestCase(800000L)] public async Task Get_File_Rpc(long byteSize) { var addedIpfsHash = AddFileToDfs(byteSize, out var crcValue, out var stream); @@ -78,21 +81,21 @@ public async Task Get_File_Rpc(long byteSize) try { - var nodePeerId = PeerIdHelper.GetPeerId("sender"); - var rpcPeerId = PeerIdHelper.GetPeerId("recipient"); + var nodeAddress = MultiAddressHelper.GetAddress("sender"); + var rpcAddress = MultiAddressHelper.GetAddress("recipient"); var peerSettings = Substitute.For(); - peerSettings.PeerId.Returns(rpcPeerId); - var nodePeer = nodePeerId; - var rpcPeer = rpcPeerId; + peerSettings.Address.Returns(rpcAddress); + var nodePeer = nodeAddress; + var rpcPeer = rpcAddress; var correlationId = CorrelationId.GenerateCorrelationId(); var fakeFileOutputPath = Path.GetTempFileName(); IDownloadFileInformation fileDownloadInformation = new DownloadFileTransferInformation(rpcPeer, nodePeer, _fakeContext.Channel, correlationId, fakeFileOutputPath, 0); - var getFileFromDfsResponseHandler = - new GetFileFromDfsResponseObserver(_logger, _fileDownloadFactory); - var transferBytesHandler = - new TransferFileBytesRequestObserver(_fileDownloadFactory, peerSettings, _logger); + GetFileFromDfsResponseObserver getFileFromDfsResponseHandler = + new(_logger, _fileDownloadFactory); + TransferFileBytesRequestObserver transferBytesHandler = + new(_fileDownloadFactory, peerSettings, _logger); _fileDownloadFactory.RegisterTransfer(fileDownloadInformation); @@ -104,7 +107,7 @@ public async Task Get_File_Rpc(long byteSize) getFileResponse.SendToHandler(_fakeContext, getFileFromDfsResponseHandler); - fileStream = await _dfs.ReadAsync(addedIpfsHash); + fileStream = await _dfsService.UnixFsApi.ReadFileAsync(addedIpfsHash.ToString()); IUploadFileInformation fileUploadInformation = new UploadFileTransferInformation( fileStream, rpcPeer, @@ -121,7 +124,7 @@ public async Task Get_File_Rpc(long byteSize) await TaskHelper.WaitForAsync(() => fileDownloadInformation.IsCompleted, TimeSpan.FromSeconds(10)); - Assert.Equal(crcValue, FileHelper.GetCrcValue(fileDownloadInformation.TempPath)); + Assert.AreEqual(crcValue, FileHelper.GetCrcValue(fileDownloadInformation.TempPath)); } finally { @@ -136,7 +139,7 @@ private MultiHash AddFileToDfs(long byteSize, out long crcValue, out Stream stre var fakeId = _hashProvider.ComputeUtf8MultiHash(CorrelationId.GenerateCorrelationId().ToString()); crcValue = FileHelper.GetCrcValue(fileToTransfer); stream = new MemoryStream(File.ReadAllBytes(fileToTransfer)); - _dfs.ReadAsync(fakeId).Returns(stream); + _dfsService.UnixFsApi.ReadFileAsync(fakeId.ToString()).Returns(stream); return fakeId; } } diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/GetInfoRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/GetInfoRequestObserverTests.cs similarity index 86% rename from src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/GetInfoRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/GetInfoRequestObserverTests.cs index f1f032368d..9643a7b2f6 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/GetInfoRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/GetInfoRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,7 +25,6 @@ using System.Linq; using System.Net; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; @@ -39,10 +38,13 @@ using Newtonsoft.Json; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; -namespace Catalyst.Core.Lib.Tests.IntegrationTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.IntegrationTests.IO.Observers { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class GetInfoRequestObserverTests { private readonly TestScheduler _testScheduler; @@ -56,7 +58,7 @@ public GetInfoRequestObserverTests() _config = new ConfigurationBuilder() .AddJsonFile(Path.Combine(Constants.ConfigSubFolder, TestConstants.TestShellNodesConfigFile)) .Build(); - + _logger = Substitute.For(); _fakeContext = Substitute.For(); @@ -65,14 +67,13 @@ public GetInfoRequestObserverTests() _fakeContext.Channel.RemoteAddress.Returns(new IPEndPoint(IPAddress.Loopback, IPEndPoint.MaxPort)); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public async Task GetInfoMessageRequest_UsingValidRequest_ShouldSendGetInfoResponse() { var protocolMessage = new GetInfoRequest { Query = true - }.ToProtocolMessage(PeerIdHelper.GetPeerId("sender")); + }.ToProtocolMessage(MultiAddressHelper.GetAddress("sender")); var expectedResponseContent = JsonConvert .SerializeObject(_config.GetSection("CatalystNodeConfiguration").AsEnumerable(), @@ -82,8 +83,8 @@ public async Task GetInfoMessageRequest_UsingValidRequest_ShouldSendGetInfoRespo protocolMessage ); - var peerSettings = PeerIdHelper.GetPeerId("sender").ToSubstitutedPeerSettings(); - var handler = new GetInfoRequestObserver( + var peerSettings = MultiAddressHelper.GetAddress("sender").ToSubstitutedPeerSettings(); + GetInfoRequestObserver handler = new( peerSettings, _config, _logger); handler.StartObserving(messageStream); @@ -93,7 +94,7 @@ public async Task GetInfoMessageRequest_UsingValidRequest_ShouldSendGetInfoRespo await _fakeContext.Channel.Received(1).WriteAndFlushAsync(Arg.Any()); var receivedCalls = _fakeContext.Channel.ReceivedCalls().ToList(); - receivedCalls.Count.Should().Be(1, + receivedCalls.Count.Should().Be(1, "the only call should be the one we checked above"); var response = ((IMessageDto) receivedCalls.Single().GetArguments()[0]) diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/SignMessageRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/SignMessageRequestObserverTests.cs similarity index 68% rename from src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/SignMessageRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/SignMessageRequestObserverTests.cs index 23ad976312..c66b102aba 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/SignMessageRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/SignMessageRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,17 +26,12 @@ using System.Linq; using System.Threading.Tasks; using Autofac; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.Keystore; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Modules.Cryptography.BulletProofs; -using Catalyst.Core.Modules.Hashing; -using Catalyst.Core.Modules.KeySigner; -using Catalyst.Core.Modules.Keystore; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Wire; @@ -47,50 +42,63 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; +using Google.Protobuf; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Core.Modules.Dfs; -namespace Catalyst.Core.Lib.Tests.IntegrationTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.IntegrationTests.IO.Observers { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class SignMessageRequestObserverTests : FileSystemBasedTest { - private readonly TestScheduler _testScheduler; - private readonly ILifetimeScope _scope; - private readonly ILogger _logger; - private readonly IKeySigner _keySigner; - private readonly IChannelHandlerContext _fakeContext; + private TestScheduler _testScheduler; + private ILifetimeScope _scope; + private ILogger _logger; + private IKeySigner _keySigner; + private IChannelHandlerContext _fakeContext; - public SignMessageRequestObserverTests(ITestOutputHelper output) : base(output, new[] + public SignMessageRequestObserverTests() : base(new[] { Path.Combine(Constants.ConfigSubFolder, TestConstants.TestShellNodesConfigFile) }) { + + } + + + [SetUp] + public void Init() + { + Setup(TestContext.CurrentContext); _testScheduler = new TestScheduler(); - ContainerProvider.ContainerBuilder.RegisterInstance(TestKeyRegistry.MockKeyRegistry()).As(); - ContainerProvider.ContainerBuilder.RegisterModule(new KeystoreModule()); - ContainerProvider.ContainerBuilder.RegisterModule(new KeySignerModule()); - ContainerProvider.ContainerBuilder.RegisterModule(new BulletProofsModule()); - ContainerProvider.ContainerBuilder.RegisterModule(new HashingModule()); + ContainerProvider.ContainerBuilder.RegisterModule(new DfsModule()); ContainerProvider.ConfigureContainerBuilder(); _scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName); _keySigner = ContainerProvider.Container.Resolve(); + var keyService = ContainerProvider.Container.Resolve(); + + keyService.SetPassphraseAsync(TestPasswordReader.BuildSecureStringPassword("test")).ConfigureAwait(false).GetAwaiter().GetResult(); + keyService.CreateAsync("self", "ed25519", 0).ConfigureAwait(false).GetAwaiter().GetResult(); + + _logger = Substitute.For(); _fakeContext = Substitute.For(); var fakeChannel = Substitute.For(); _fakeContext.Channel.Returns(fakeChannel); } - [Theory] - [InlineData("Hello Catalyst")] - [InlineData("")] - [InlineData("Hello&?!1253Catalyst")] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [TestCase("Hello Catalyst")] + [TestCase("")] + [TestCase("Hello&?!1253Catalyst")] + public async Task RpcServer_Can_Handle_SignMessageRequest(string message) { - var sender = PeerIdHelper.GetPeerId("sender"); + var sender = MultiAddressHelper.GetAddress("sender"); var peerSettings = Substitute.For(); - peerSettings.PeerId.Returns(sender); + peerSettings.Address.Returns(sender); var signMessageRequest = new SignMessageRequest { Message = message.ToUtf8ByteString(), @@ -100,13 +108,13 @@ public async Task RpcServer_Can_Handle_SignMessageRequest(string message) signMessageRequest.ToProtocolMessage(sender); await TaskHelper.WaitForAsync( - () => _keySigner.KeyStore.KeyStoreDecrypt(KeyRegistryTypes.DefaultKey) != null, - TimeSpan.FromSeconds(2)); + () => _keySigner.GetPrivateKey(KeyRegistryTypes.DefaultKey) != null, + TimeSpan.FromSeconds(2)); var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, _testScheduler, protocolMessage); - var handler = - new SignMessageRequestObserver(peerSettings, _logger, _keySigner); + SignMessageRequestObserver handler = + new(peerSettings, _logger, _keySigner); handler.StartObserving(messageStream); @@ -119,7 +127,7 @@ await TaskHelper.WaitForAsync( var sentResponseDto = (IMessageDto) receivedCalls.Single().GetArguments().Single(); var signResponseMessage = sentResponseDto.Content.FromProtocolMessage(); - signResponseMessage.OriginalMessage.Should().Equal(message); + signResponseMessage.OriginalMessage.Should().Equal(ByteString.CopyFromUtf8(message)); signResponseMessage.Signature.Should().NotBeEmpty(); signResponseMessage.PublicKey.Should().NotBeEmpty(); } diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/VerifyMessageRequestObserverIntegrationTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/VerifyMessageRequestObserverIntegrationTests.cs similarity index 79% rename from src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/VerifyMessageRequestObserverIntegrationTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/VerifyMessageRequestObserverIntegrationTests.cs index ad3ad39d77..65bee54352 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Observers/VerifyMessageRequestObserverIntegrationTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Observers/VerifyMessageRequestObserverIntegrationTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,6 @@ #endregion using Autofac; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.Types; using Catalyst.Protocol.Rpc.Node; @@ -32,39 +31,47 @@ using Google.Protobuf; using NSubstitute; using System.Linq; +using Catalyst.Abstractions.Config; using Catalyst.Abstractions.Keystore; +using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Core.Modules.Consensus; using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.Core.Modules.Dfs; using Catalyst.Core.Modules.KeySigner; using Catalyst.Core.Modules.Keystore; -using Catalyst.Core.Modules.Rpc.Server; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; using Catalyst.Core.Modules.Mempool; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Network; -using Catalyst.Protocol.Peer; using Catalyst.Core.Modules.Authentication; using Catalyst.Core.Modules.Hashing; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using MultiFormats; -namespace Catalyst.Core.Lib.Tests.IntegrationTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.IntegrationTests.IO.Observers { public class VerifyMessageRequestObserverIntegrationTests : FileSystemBasedTest { - private readonly IKeySigner _keySigner; - private readonly IChannelHandlerContext _fakeContext; - private readonly IRpcRequestObserver _verifyMessageRequestObserver; - private readonly ILifetimeScope _scope; - private readonly PeerId _peerId; - private readonly ByteString _testMessageToSign; - - public VerifyMessageRequestObserverIntegrationTests(ITestOutputHelper output) : base(output) + private IKeySigner _keySigner; + private IChannelHandlerContext _fakeContext; + private IRpcRequestObserver _verifyMessageRequestObserver; + private ILifetimeScope _scope; + private MultiAddress _address; + private ByteString _testMessageToSign; + + [SetUp] + public void Init() { + Setup(TestContext.CurrentContext); + _testMessageToSign = ByteString.CopyFromUtf8("TestMsg"); + + var networkTypeProvider = Substitute.For(); + networkTypeProvider.NetworkType.Returns(NetworkType.Devnet); + ContainerProvider.ContainerBuilder.RegisterInstance(networkTypeProvider).As(); ContainerProvider.ContainerBuilder.RegisterInstance(TestKeyRegistry.MockKeyRegistry()).As(); ContainerProvider.ContainerBuilder.RegisterModule(new KeystoreModule()); @@ -77,27 +84,24 @@ public VerifyMessageRequestObserverIntegrationTests(ITestOutputHelper output) : ContainerProvider.ContainerBuilder.RegisterModule(new AuthenticationModule()); ContainerProvider.ContainerBuilder.RegisterModule(new HashingModule()); ContainerProvider.ContainerBuilder.RegisterType().As(); - - ContainerProvider.ContainerBuilder.RegisterInstance(PeerIdHelper.GetPeerId("Test")) - .As(); + ContainerProvider.ContainerBuilder.RegisterInstance(MultiAddressHelper.GetAddress("Test")).As(); ContainerProvider.ConfigureContainerBuilder(); _scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName); _keySigner = ContainerProvider.Container.Resolve(); - _peerId = ContainerProvider.Container.Resolve(); + _address = ContainerProvider.Container.Resolve(); _fakeContext = Substitute.For(); - + var fakeChannel = Substitute.For(); _fakeContext.Channel.Returns(fakeChannel); _verifyMessageRequestObserver = _scope.Resolve(); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Valid_Message_Signature_Can_Return_True_Response() { - var privateKey = _keySigner.KeyStore.KeyStoreDecrypt(KeyRegistryTypes.DefaultKey); + var privateKey = _keySigner.GetPrivateKey(KeyRegistryTypes.DefaultKey); var signingContext = new SigningContext { @@ -115,12 +119,11 @@ public void Valid_Message_Signature_Can_Return_True_Response() _verifyMessageRequestObserver .OnNext(new ObserverDto(_fakeContext, - requestMessage.ToProtocolMessage(_peerId))); + requestMessage.ToProtocolMessage(_address))); AssertVerifyResponse(true); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Invalid_Message_Signature_Can_Return_False_Response() { var requestMessage = new VerifyMessageRequest @@ -133,7 +136,7 @@ public void Invalid_Message_Signature_Can_Return_False_Response() _verifyMessageRequestObserver .OnNext(new ObserverDto(_fakeContext, - requestMessage.ToProtocolMessage(_peerId))); + requestMessage.ToProtocolMessage(_address))); AssertVerifyResponse(false); } diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Transport/Channels/RpcClientChannelFactoryTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Transport/Channels/RpcClientChannelFactoryTests.cs similarity index 72% rename from src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Transport/Channels/RpcClientChannelFactoryTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Transport/Channels/RpcClientChannelFactoryTests.cs index 4a232d545b..a97bd541cd 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Transport/Channels/RpcClientChannelFactoryTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Transport/Channels/RpcClientChannelFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,49 +24,52 @@ using System.Linq; using System.Threading.Tasks; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Rpc.Authentication; using Catalyst.Abstractions.Rpc.IO.Messaging.Correlation; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.Protocol.Network; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using DotNetty.Buffers; using DotNetty.Transport.Channels.Embedded; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; - -namespace Catalyst.Core.Lib.Tests.IntegrationTests.Rpc.IO.Transport.Channels +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Transport.Channels; + +namespace Catalyst.Core.Modules.Rpc.Client.Tests.IntegrationTests.Rpc.IO.Transport.Channels { public sealed class RpcClientChannelFactoryTests { - private readonly TestScheduler _testScheduler; - private readonly UnitTests.Rpc.IO.Transport.Channels.RpcServerChannelFactoryTests.TestRpcServerChannelFactory _serverFactory; - private readonly EmbeddedChannel _serverChannel; - private readonly EmbeddedChannel _clientChannel; - private readonly IRpcMessageCorrelationManager _clientCorrelationManager; - private readonly IKeySigner _clientKeySigner; - private readonly IAuthenticationStrategy _authenticationStrategy; - private readonly IPeerIdValidator _peerIdValidator; - private readonly IKeySigner _serverKeySigner; - private readonly IRpcMessageCorrelationManager _serverCorrelationManager; - - public RpcClientChannelFactoryTests() + private TestScheduler _testScheduler; + private RpcServerChannelFactoryTests.TestRpcServerChannelFactory _serverFactory; + private EmbeddedChannel _serverChannel; + private EmbeddedChannel _clientChannel; + private IRpcMessageCorrelationManager _clientCorrelationManager; + private FakeKeySigner _clientKeySigner; + private IAuthenticationStrategy _authenticationStrategy; + private IPeerIdValidator _peerIdValidator; + private FakeKeySigner _serverKeySigner; + private IRpcMessageCorrelationManager _serverCorrelationManager; + + [SetUp] + public void Init() { _testScheduler = new TestScheduler(); _serverCorrelationManager = Substitute.For(); - _serverKeySigner = Substitute.For(); + _serverKeySigner = Substitute.For(); _serverKeySigner.CryptoContext.SignatureLength.Returns(64); _authenticationStrategy = Substitute.For(); @@ -75,7 +78,7 @@ public RpcClientChannelFactoryTests() var peerSettings = Substitute.For(); peerSettings.NetworkType.Returns(NetworkType.Devnet); - _serverFactory = new UnitTests.Rpc.IO.Transport.Channels.RpcServerChannelFactoryTests.TestRpcServerChannelFactory( + _serverFactory = new RpcServerChannelFactoryTests.TestRpcServerChannelFactory( _serverCorrelationManager, _serverKeySigner, _authenticationStrategy, @@ -84,10 +87,10 @@ public RpcClientChannelFactoryTests() _testScheduler); _clientCorrelationManager = Substitute.For(); - _clientKeySigner = Substitute.For(); + _clientKeySigner = Substitute.For(); _clientKeySigner.CryptoContext.SignatureLength.Returns(64); var clientFactory = - new UnitTests.Rpc.IO.Transport.Channels.RpcClientChannelFactoryTests.TestRpcClientChannelFactory( + new UnitTests.IO.Transport.Channels.RpcClientChannelFactoryTests.TestRpcClientChannelFactory( _clientKeySigner, _clientCorrelationManager, _peerIdValidator, @@ -101,24 +104,23 @@ public RpcClientChannelFactoryTests() new EmbeddedChannel("client".ToChannelId(), true, clientFactory.InheritedHandlers.ToArray()); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public async Task RpcClientChannelFactory_Pipeline_Should_Produce_Request_Object_RpcServerChannelFactory_Can_Process_Into_Observable() { - var recipient = PeerIdHelper.GetPeerId("recipient"); - var sender = PeerIdHelper.GetPeerId("sender"); + var recipient = MultiAddressHelper.GetAddress("recipient"); + var sender = MultiAddressHelper.GetAddress("sender"); var signature = Substitute.For(); signature.SignatureBytes.Returns(ByteUtil.GenerateRandomByteArray(new FfiWrapper().SignatureLength)); - _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); + _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); _clientKeySigner.Sign(Arg.Any(), default).ReturnsForAnyArgs(signature); var correlationId = CorrelationId.GenerateCorrelationId(); var protocolMessage = new GetPeerCountRequest().ToProtocolMessage(sender, correlationId); - var dto = new MessageDto( + MessageDto dto = new( protocolMessage, recipient ); @@ -143,12 +145,11 @@ public async Task ) .ReturnsForAnyArgs(true); - _authenticationStrategy.Authenticate(Arg.Any()).Returns(true); + _authenticationStrategy.Authenticate(Arg.Any()).Returns(true); - var observer = new ProtocolMessageObserver(0, Substitute.For()); + ProtocolMessageObserver> observer = new(0, Substitute.For()); - var messageStream = _serverFactory.InheritedHandlers.OfType().Single() - .MessageStream; + var messageStream = _serverFactory.InheritedHandlers.OfType().Single().MessageStream; using (messageStream.Subscribe(observer)) { diff --git a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Transport/Channels/RpcServerChannelFactoryTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Transport/Channels/RpcServerChannelFactoryTests.cs similarity index 75% rename from src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Transport/Channels/RpcServerChannelFactoryTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Transport/Channels/RpcServerChannelFactoryTests.cs index a1c7b061c5..68c1088bbd 100644 --- a/src/Catalyst.Core.Lib.Tests/IntegrationTests/Rpc/IO/Transport/Channels/RpcServerChannelFactoryTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/IntergrationTests/IO/Transport/Channels/RpcServerChannelFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,47 +24,50 @@ using System.Linq; using System.Threading.Tasks; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Rpc.Authentication; using Catalyst.Abstractions.Rpc.IO.Messaging.Correlation; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Protocol.Network; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using DotNetty.Buffers; using DotNetty.Transport.Channels.Embedded; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; - -namespace Catalyst.Core.Lib.Tests.IntegrationTests.Rpc.IO.Transport.Channels +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Core.Modules.Rpc.Client.Tests.UnitTests.IO.Transport.Channels; +using static Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Transport.Channels.RpcServerChannelFactoryTests; + +namespace Catalyst.Core.Modules.Rpc.Server.Tests.IntegrationTests.IO.Transport.Channels { public sealed class RpcServerChannelFactoryTests { private readonly TestScheduler _testScheduler; - private readonly UnitTests.Rpc.IO.Transport.Channels.RpcClientChannelFactoryTests.TestRpcClientChannelFactory _clientFactory; + private readonly RpcClientChannelFactoryTests.TestRpcClientChannelFactory _clientFactory; private readonly EmbeddedChannel _serverChannel; private readonly EmbeddedChannel _clientChannel; private readonly IRpcMessageCorrelationManager _clientCorrelationManager; - private readonly IKeySigner _clientKeySigner; + private readonly FakeKeySigner _clientKeySigner; private readonly IAuthenticationStrategy _authenticationStrategy; private readonly IPeerIdValidator _peerIdValidator; - private readonly IKeySigner _serverKeySigner; + private readonly FakeKeySigner _serverKeySigner; private readonly IRpcMessageCorrelationManager _serverCorrelationManager; public RpcServerChannelFactoryTests() { _testScheduler = new TestScheduler(); _serverCorrelationManager = Substitute.For(); - _serverKeySigner = Substitute.For(); + _serverKeySigner = Substitute.For(); _serverKeySigner.CryptoContext.SignatureLength.Returns(64); _authenticationStrategy = Substitute.For(); @@ -73,7 +76,7 @@ public RpcServerChannelFactoryTests() var peerSettings = Substitute.For(); peerSettings.NetworkType.Returns(NetworkType.Devnet); - var serverFactory = new UnitTests.Rpc.IO.Transport.Channels.RpcServerChannelFactoryTests.TestRpcServerChannelFactory( + TestRpcServerChannelFactory serverFactory = new( _serverCorrelationManager, _serverKeySigner, _authenticationStrategy, @@ -82,11 +85,11 @@ public RpcServerChannelFactoryTests() _testScheduler); _clientCorrelationManager = Substitute.For(); - _clientKeySigner = Substitute.For(); + _clientKeySigner = Substitute.For(); _clientKeySigner.CryptoContext.SignatureLength.Returns(64); - _clientFactory = new UnitTests.Rpc.IO.Transport.Channels.RpcClientChannelFactoryTests.TestRpcClientChannelFactory( - _clientKeySigner, + _clientFactory = new RpcClientChannelFactoryTests.TestRpcClientChannelFactory( + _clientKeySigner, _clientCorrelationManager, _peerIdValidator, peerSettings, @@ -94,59 +97,59 @@ public RpcServerChannelFactoryTests() _serverChannel = new EmbeddedChannel("server".ToChannelId(), true, serverFactory.InheritedHandlers.ToArray()); - + _clientChannel = new EmbeddedChannel("client".ToChannelId(), true, _clientFactory.InheritedHandlers.ToArray()); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] + public async Task RpcServerChannelFactory_Pipeline_Should_Produce_Response_Object_RpcClientChannelFactory_Can_Process() { - var recipient = PeerIdHelper.GetPeerId("recipient"); - var sender = PeerIdHelper.GetPeerId("sender"); + var recipient = MultiAddressHelper.GetAddress("recipient"); + var sender = MultiAddressHelper.GetAddress("sender"); var signature = Substitute.For(); - _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); - + _peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); + _serverKeySigner.Sign(Arg.Any(), default).ReturnsForAnyArgs(signature); - + var correlationId = CorrelationId.GenerateCorrelationId(); var protocolMessage = new GetPeerCountResponse().ToProtocolMessage(sender, correlationId); - var dto = new MessageDto( + MessageDto dto = new( protocolMessage, recipient ); _clientCorrelationManager.TryMatchResponse(Arg.Any()).Returns(true); - + _serverChannel.WriteOutbound(dto); var sentBytes = _serverChannel.ReadOutbound(); _serverCorrelationManager.DidNotReceiveWithAnyArgs().AddPendingRequest(Arg.Any>()); - + _serverKeySigner.ReceivedWithAnyArgs(1) .Sign(Arg.Any(), default); - + _clientKeySigner.Verify( Arg.Any(), - Arg.Any(), + Arg.Any(), default) .ReturnsForAnyArgs(true); - - _authenticationStrategy.Authenticate(Arg.Any()).Returns(true); - - var observer = new ProtocolMessageObserver(0, Substitute.For()); - var messageStream = _clientFactory.InheritedHandlers.OfType().Single().MessageStream; - + _authenticationStrategy.Authenticate(Arg.Any()).Returns(true); + + ProtocolMessageObserver> observer = new(0, Substitute.For()); + + var messageStream = _clientFactory.InheritedHandlers.OfType().Single().MessageStream; + using (messageStream.Subscribe(observer)) { _clientChannel.WriteInbound(sentBytes); _clientChannel.ReadInbound(); _clientCorrelationManager.ReceivedWithAnyArgs(1).TryMatchResponse(Arg.Any()); - + _clientKeySigner.ReceivedWithAnyArgs(1).Verify(null, null, null); _testScheduler.Start(); @@ -154,7 +157,7 @@ public async Task observer.Received.Count.Should().Be(1); observer.Received.Single().Payload.CorrelationId.ToCorrelationId().Id.Should().Be(correlationId.Id); } - + await _serverChannel.DisconnectAsync(); await _clientChannel.DisconnectAsync(); } diff --git a/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/AddFileToDfsRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/AddFileToDfsRequestObserverTests.cs similarity index 80% rename from src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/AddFileToDfsRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/AddFileToDfsRequestObserverTests.cs index 3f9a925d33..1f0e6f77c5 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/AddFileToDfsRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/AddFileToDfsRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,59 +25,62 @@ using System.IO; using System.Threading; using Catalyst.Abstractions.Dfs; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.IO.Messaging.Correlation; +using Catalyst.Abstractions.Options; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; using FluentAssertions; +using MultiFormats.Registry; using NSubstitute; using NSubstitute.ExceptionExtensions; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; //@TODO should be in rpc module test -namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { public sealed class AddFileToDfsRequestObserverTests { - private readonly ManualResetEvent _manualResetEvent; - private readonly IChannelHandlerContext _fakeContext; - private readonly IDownloadFileTransferFactory _nodeFileTransferFactory; - private readonly AddFileToDfsRequestObserver _addFileToDfsRequestObserver; - private readonly PeerId _senderIdentifier; - private readonly IDfs _fakeDfs; - private readonly IHashProvider _hashProvider; - - public AddFileToDfsRequestObserverTests() + private ManualResetEvent _manualResetEvent; + private IChannelHandlerContext _fakeContext; + private IDownloadFileTransferFactory _nodeFileTransferFactory; + private AddFileToDfsRequestObserver _addFileToDfsRequestObserver; + private MultiAddress _senderAddress; + private IDfsService _fakeDfsService; + private IHashProvider _hashProvider; + + [SetUp] + public void Init() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); _manualResetEvent = new ManualResetEvent(false); - _senderIdentifier = PeerIdHelper.GetPeerId("sender"); - var peerSettings = _senderIdentifier.ToSubstitutedPeerSettings(); - _fakeDfs = Substitute.For(); + _senderAddress = MultiAddressHelper.GetAddress("sender"); + var peerSettings = _senderAddress.ToSubstitutedPeerSettings(); + _fakeDfsService = Substitute.For(); var logger = Substitute.For(); _fakeContext = Substitute.For(); _nodeFileTransferFactory = Substitute.For(); - _addFileToDfsRequestObserver = new AddFileToDfsRequestObserver(_fakeDfs, + _addFileToDfsRequestObserver = new AddFileToDfsRequestObserver(_fakeDfsService, peerSettings, _nodeFileTransferFactory, + _hashProvider, logger); } - [Fact] + [Test] public void Handler_Uses_Correct_CorrelationId() { _nodeFileTransferFactory.RegisterTransfer(Arg.Any()) @@ -94,7 +97,7 @@ public void Handler_Uses_Correct_CorrelationId() info => info.CorrelationId.Id.Equals(correlationId.Id))); } - [Fact] + [Test] public void Handler_Can_Initialize_Download_File_Transfer() { _nodeFileTransferFactory.RegisterTransfer(Arg.Any()) @@ -109,7 +112,7 @@ public void Handler_Can_Initialize_Download_File_Transfer() t => t.Content.FromProtocolMessage().ResponseCode[0] == FileTransferResponseCodeTypes.Successful.Id)); } - [Fact] + [Test] public void Handler_Sends_Error_On_Invalid_Message() { _nodeFileTransferFactory.RegisterTransfer(Arg.Any()).Throws(new Exception()); @@ -123,14 +126,16 @@ public void Handler_Sends_Error_On_Invalid_Message() t => t.Content.FromProtocolMessage().ResponseCode[0] == FileTransferResponseCodeTypes.Error.Id)); } - [Fact] + [Test] public void Successful_Add_File_Can_Respond_With_Finished_Code() { _nodeFileTransferFactory.RegisterTransfer(Arg.Any()) .Returns(FileTransferResponseCodeTypes.Successful); - var expectedCid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("expectedHash")); - _fakeDfs.AddAsync(Arg.Any(), Arg.Any()).Returns(expectedCid); + var expectedCid = _hashProvider.ComputeUtf8MultiHash("expectedHash").ToCid(); + var fakeBlock = Substitute.For(); + fakeBlock.Id.Returns(expectedCid); + _fakeDfsService.UnixFsApi.AddAsync(Arg.Any(), Arg.Any(), Arg.Any()).Returns(fakeBlock); var protocolMessage = GenerateProtocolMessage(); @@ -157,13 +162,13 @@ await information.RecipientChannel.WriteAndFlushAsync(Arg.Do(x => addFileToDfsResponse.DfsHash.Should().Be(expectedCid); } - [Fact] + [Test] public void Dfs_Failure_Can_Respond_With_Failed_Code() { _nodeFileTransferFactory.RegisterTransfer(Arg.Any()) .Returns(FileTransferResponseCodeTypes.Successful); - _fakeDfs.AddAsync(Arg.Any(), Arg.Any()).Throws(new Exception()); + _fakeDfsService.UnixFsApi.AddAsync(Arg.Any(), Arg.Any()).Throws(new Exception()); var protocolMessage = GenerateProtocolMessage(); @@ -196,7 +201,7 @@ private ProtocolMessage GenerateProtocolMessage(ICorrelationId correlationId = n Node = "node1", FileName = "Test.dat", FileSize = 10000 - }.ToProtocolMessage(_senderIdentifier, correlationId); + }.ToProtocolMessage(_senderAddress, correlationId); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/BroadcastRawTransactionRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/BroadcastRawTransactionRequestObserverTests.cs similarity index 77% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/BroadcastRawTransactionRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/BroadcastRawTransactionRequestObserverTests.cs index 9da1c6ef5d..a3ace19daa 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/BroadcastRawTransactionRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/BroadcastRawTransactionRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,17 +23,17 @@ using Catalyst.Abstractions.IO.Events; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; using Catalyst.Protocol.Rpc.Node; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; using NSubstitute; +using NUnit.Framework; using Serilog; -using Xunit; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { public class BroadcastRawTransactionRequestObserverTests { @@ -44,7 +44,7 @@ public BroadcastRawTransactionRequestObserverTests() { _transactionReceivedEvent = Substitute.For(); - var peerSettings = PeerIdHelper.GetPeerId("Test").ToSubstitutedPeerSettings(); + var peerSettings = MultiAddressHelper.GetAddress("Test").ToSubstitutedPeerSettings(); _broadcastRawTransactionRequestObserver = new BroadcastRawTransactionRequestObserver( Substitute.For(), @@ -53,23 +53,23 @@ public BroadcastRawTransactionRequestObserverTests() } [Theory] - [InlineData(ResponseCode.Pending)] - [InlineData(ResponseCode.Error)] - [InlineData(ResponseCode.Successful)] - [InlineData(ResponseCode.Failed)] - [InlineData(ResponseCode.Finished)] + [TestCase(ResponseCode.Pending)] + [TestCase(ResponseCode.Error)] + [TestCase(ResponseCode.Successful)] + [TestCase(ResponseCode.Failed)] + [TestCase(ResponseCode.Finished)] public void Can_Respond_With_Correct_Response(ResponseCode expectedResponse) { var channelContext = Substitute.For(); var channel = Substitute.For(); channelContext.Channel.Returns(channel); - _transactionReceivedEvent.OnTransactionReceived(Arg.Any()) + _transactionReceivedEvent.OnTransactionReceived(Arg.Any(), Arg.Any()) .Returns(expectedResponse); _broadcastRawTransactionRequestObserver .OnNext(new ObserverDto(channelContext, - new BroadcastRawTransactionRequest {Transaction = new TransactionBroadcast()}.ToProtocolMessage( - PeerIdHelper.GetPeerId("FakeSender")))); + new BroadcastRawTransactionRequest { Transaction = new TransactionBroadcast() }.ToProtocolMessage( + MultiAddressHelper.GetAddress("FakeSender")))); channelContext.Channel.Received(1)?.WriteAndFlushAsync( Arg.Is(transactionObj => ((MessageDto) transactionObj) diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/GetDeltaRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetDeltaRequestObserverTests.cs similarity index 75% rename from src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/GetDeltaRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetDeltaRequestObserverTests.cs index e12bef999c..a1cc664c91 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/P2P/IO/Observers/GetDeltaRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetDeltaRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,9 +26,8 @@ using Autofac; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Hashing; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; using Catalyst.Protocol.Deltas; @@ -36,25 +35,27 @@ using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; -using LibP2P; +using Lib.P2P; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; -namespace Catalyst.Core.Lib.Tests.UnitTests.P2P.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { public sealed class GetDeltaRequestObserverTests { - private readonly TestScheduler _testScheduler; - private readonly IDeltaCache _deltaCache; - private readonly GetDeltaRequestObserver _observer; - private readonly IChannelHandlerContext _fakeContext; - private readonly IHashProvider _hashProvider; - - public GetDeltaRequestObserverTests() + private TestScheduler _testScheduler; + private IDeltaCache _deltaCache; + private GetDeltaRequestObserver _observer; + private IChannelHandlerContext _fakeContext; + private IHashProvider _hashProvider; + + [SetUp] + public void Init() { - var builder = new ContainerBuilder(); + ContainerBuilder builder = new(); builder.RegisterModule(); var container = builder.Build(); container.BeginLifetimeScope(); @@ -63,17 +64,17 @@ public GetDeltaRequestObserverTests() _testScheduler = new TestScheduler(); var logger = Substitute.For(); - var peerIdentifier = PeerIdHelper.GetPeerId("responder"); - var peerSettings = peerIdentifier.ToSubstitutedPeerSettings(); + var peerAddress = MultiAddressHelper.GetAddress("responder"); + var peerSettings = peerAddress.ToSubstitutedPeerSettings(); _deltaCache = Substitute.For(); _observer = new GetDeltaRequestObserver(_deltaCache, peerSettings, logger); _fakeContext = Substitute.For(); } - [Fact] + [Test] public async Task GetDeltaRequestObserver_Should_Send_Response_When_Delta_Found_In_Cache() { - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("abcd")); + var cid = _hashProvider.ComputeUtf8MultiHash("abcd").ToCid(); var delta = CreateAndExpectDeltaFromCache(cid); var observable = CreateStreamWithDeltaRequest(cid); @@ -90,10 +91,10 @@ await _fakeContext.Channel.ReceivedWithAnyArgs(1) delta.PreviousDeltaDfsHash)); } - [Fact] + [Test] public async Task GetDeltaRequestObserver_Should_Send_Response_With_Null_Content_If_Not_Retrieved_In_Cache() { - var cid = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("defg")); + var cid = _hashProvider.ComputeUtf8MultiHash("defg").ToCid(); var observable = CreateStreamWithDeltaRequest(cid); @@ -111,9 +112,9 @@ await _fakeContext.Channel.ReceivedWithAnyArgs(1) private IObservable> CreateStreamWithDeltaRequest(Cid cid) { - var deltaRequest = new GetDeltaRequest {DeltaDfsHash = cid.ToArray().ToByteString()}; + var deltaRequest = new GetDeltaRequest { DeltaDfsHash = cid.ToArray().ToByteString() }; - var message = deltaRequest.ToProtocolMessage(PeerIdHelper.GetPeerId("sender")); + var message = deltaRequest.ToProtocolMessage(MultiAddressHelper.GetAddress("sender")); var observable = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, _testScheduler, message); return observable; @@ -125,10 +126,10 @@ private Delta CreateAndExpectDeltaFromCache(Cid cid) _deltaCache.TryGetOrAddConfirmedDelta(Arg.Is(cid), out Arg.Any()) .Returns(ci => - { - ci[1] = delta; - return true; - }); + { + ci[1] = delta; + return true; + }); return delta; } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetFileFromDfsRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetFileFromDfsRequestObserverTests.cs similarity index 63% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetFileFromDfsRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetFileFromDfsRequestObserverTests.cs index b6d9bf66e3..8d7e4412d0 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetFileFromDfsRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetFileFromDfsRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,74 +25,72 @@ using System.IO; using System.Threading; using Catalyst.Abstractions.Dfs; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; using Catalyst.Protocol.Rpc.Node; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; -using LibP2P; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { public sealed class GetFileFromDfsRequestObserverTests : IDisposable { - private readonly IHashProvider _hashProvider; - private readonly IUploadFileTransferFactory _fileTransferFactory; - private readonly IDfs _dfs; - private readonly GetFileFromDfsRequestObserver _observer; + private IHashProvider _hashProvider; + private IUploadFileTransferFactory _fileTransferFactory; + private IDfsService _dfsService; + private GetFileFromDfsRequestObserver _observer; - public GetFileFromDfsRequestObserverTests() + [SetUp] + public void Init() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); _fileTransferFactory = Substitute.For(); - _dfs = Substitute.For(); - var peerSettings = PeerIdHelper.GetPeerId("test").ToSubstitutedPeerSettings(); - _observer = new GetFileFromDfsRequestObserver(_dfs, peerSettings, _fileTransferFactory, + _dfsService = Substitute.For(); + var peerSettings = MultiAddressHelper.GetAddress("test").ToSubstitutedPeerSettings(); + _observer = new GetFileFromDfsRequestObserver(_dfsService, peerSettings, _fileTransferFactory, Substitute.For()); } - [Fact] + [Test] public void GetFileRequestHandlerInitializesFileUpload() { using (GetFakeDfsStream(FileTransferResponseCodeTypes.Successful)) { _observer.OnNext(GetFileFromDfsRequestMessage()); - _dfs.Received(1)?.ReadAsync(Arg.Any()); + _dfsService.UnixFsApi.Received(1)?.ReadFileAsync(Arg.Any()); _fileTransferFactory.Received(1)? .FileTransferAsync(Arg.Any(), Arg.Any()); } } - [Fact] + [Test] public void FileTransferStreamIsDisposedOnError() { - using (var fakeStream = GetFakeDfsStream(FileTransferResponseCodeTypes.Error)) - { - var message = GetFileFromDfsRequestMessage(); - _observer.OnNext(message); - Assert.False(fakeStream.CanRead); - } + using var fakeStream = GetFakeDfsStream(FileTransferResponseCodeTypes.Error); + var message = GetFileFromDfsRequestMessage(); + _observer.OnNext(message); + Assert.False(fakeStream.CanRead); } private MemoryStream GetFakeDfsStream(FileTransferResponseCodeTypes fakeResponse) { - var fakeStream = new MemoryStream(); + MemoryStream fakeStream = new(); fakeStream.Write(new byte[50]); - _dfs.ReadAsync(Arg.Any()).Returns(fakeStream); + _dfsService.UnixFsApi.ReadFileAsync(Arg.Any()).Returns(fakeStream); _fileTransferFactory.RegisterTransfer(Arg.Any()).Returns(fakeResponse); return fakeStream; } @@ -101,10 +99,10 @@ private IObserverDto GetFileFromDfsRequestMessage() { var getFileFromDfsRequestMessage = new GetFileFromDfsRequest { - DfsHash = CidHelper.CreateCid(_hashProvider.ComputeUtf8MultiHash("test")) + DfsHash = _hashProvider.ComputeUtf8MultiHash("test").ToCid() }; var protocolMessage = getFileFromDfsRequestMessage - .ToProtocolMessage(PeerIdHelper.GetPeerId("TestMan"), CorrelationId.GenerateCorrelationId()); + .ToProtocolMessage(MultiAddressHelper.GetAddress("TestMan"), CorrelationId.GenerateCorrelationId()); return new ObserverDto(Substitute.For(), protocolMessage); } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetMempoolRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetMempoolRequestObserverTests.cs similarity index 67% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetMempoolRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetMempoolRequestObserverTests.cs index 036f42c0d3..800ceb5a59 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetMempoolRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetMempoolRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,31 +24,34 @@ using System.Collections.Generic; using System.Linq; using System.Net; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.Mempool; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; using Catalyst.Protocol.Rpc.Node; +using Catalyst.Protocol.Transaction; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; +using NUnit.Framework; using Serilog; -using Xunit; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { public sealed class GetMempoolRequestObserverTests { - private readonly ILogger _logger; - private readonly IChannelHandlerContext _fakeContext; - private readonly TestMapperProvider _mapperProvider; + private ILogger _logger; + private IChannelHandlerContext _fakeContext; + private TestMapperProvider _mapperProvider; - public GetMempoolRequestObserverTests() - { + [SetUp] + public void Init() + { _logger = Substitute.For(); _fakeContext = Substitute.For(); var fakeChannel = Substitute.For(); @@ -61,36 +64,35 @@ public GetMempoolRequestObserverTests() new List { new object[] {CreateTestTransactions()}, - new object[] {new List()} + new object[] {new List()} }; - private static List CreateTestTransactions() + private static List CreateTestTransactions() { - var mapperProvider = new TestMapperProvider(); + TestMapperProvider mapperProvider = new(); var txLst = new List { TransactionHelper.GetPublicTransaction(234, "standardPubKey", "sign1"), TransactionHelper.GetPublicTransaction(567, "standardPubKey", "sign2") - }.Select(x => x.ToDao(mapperProvider)).ToList(); + }.Select(x => x.PublicEntry.ToDao(mapperProvider)).ToList(); return txLst; } - [Theory] - [MemberData(nameof(MempoolTransactions))] - public void GetMempool_UsingFilledMempool_ShouldSendGetMempoolResponse(List mempoolTransactions) + [TestCaseSource(nameof(MempoolTransactions))] + public void GetMempool_UsingFilledMempool_ShouldSendGetMempoolResponse(List mempoolTransactions) { - var testScheduler = new TestScheduler(); - var mempool = Substitute.For>(); - mempool.Repository.GetAll().Returns(mempoolTransactions); + TestScheduler testScheduler = new(); + var mempool = Substitute.For>(); + mempool.Service.GetAll().Returns(mempoolTransactions); - var protocolMessage = new GetMempoolRequest().ToProtocolMessage(PeerIdHelper.GetPeerId("sender_key")); + var protocolMessage = new GetMempoolRequest().ToProtocolMessage(MultiAddressHelper.GetAddress("sender_key")); var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, protocolMessage); - var peerSettings = PeerIdHelper.GetPeerId("sender").ToSubstitutedPeerSettings(); - var handler = new GetMempoolRequestObserver(peerSettings, mempool, _mapperProvider, _logger); + var peerSettings = MultiAddressHelper.GetAddress("sender").ToSubstitutedPeerSettings(); + GetMempoolRequestObserver handler = new(peerSettings, mempool, _mapperProvider, _logger); handler.StartObserving(messageStream); @@ -103,7 +105,7 @@ public void GetMempool_UsingFilledMempool_ShouldSendGetMempoolResponse(List(); - responseContent.Transactions.Select(x => x.ToDao(_mapperProvider)).Should() + responseContent.Transactions.Select(x => x.ToDao(_mapperProvider)).Should() .BeEquivalentTo(mempoolTransactions); } } diff --git a/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetPeerInfoRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetPeerInfoRequestObserverTests.cs new file mode 100644 index 0000000000..84b4f5ef35 --- /dev/null +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetPeerInfoRequestObserverTests.cs @@ -0,0 +1,166 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.P2P.Models; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Core.Modules.Rpc.Server.IO.Observers; +using Catalyst.Protocol.Rpc.Node; +using Catalyst.Protocol.Wire; +using Catalyst.TestUtils; +using FluentAssertions; +using Microsoft.Reactive.Testing; +using NSubstitute; +using Serilog; +using SharpRepository.InMemoryRepository; +using NUnit.Framework; +using Catalyst.Core.Lib.P2P.Repository; +using MultiFormats; +using Catalyst.Abstractions.P2P; +using DotNetty.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; + +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers +{ + /// + /// Tests the get peer info calls + /// + public sealed class GetPeerInfoRequestObserverTests + { + private ILogger _logger; + private IChannelHandlerContext _fakeContext; + private IPeerRepository _peerRepository; + + [SetUp] + public void Init() + { + _logger = Substitute.For(); + _fakeContext = Substitute.For(); + + var fakeChannel = Substitute.For(); + _fakeContext.Channel.Returns(fakeChannel); + + _peerRepository = new PeerRepository(new InMemoryRepository()); + _peerRepository.Add(GetPeerTestData()); + } + + public IEnumerable GetPeerTestData() + { + yield return new Peer + { + Address = + MultiAddressHelper.GetAddress("publickey-1", IPAddress.Parse("172.0.0.1"), 9090), + Reputation = 0, + LastSeen = DateTime.UtcNow, + Created = DateTime.UtcNow + }; + yield return new Peer + { + Address = + MultiAddressHelper.GetAddress("publickey-2", IPAddress.Parse("172.0.0.2"), 9090), + Reputation = 1, + LastSeen = DateTime.UtcNow, + Created = DateTime.UtcNow + }; + yield return new Peer + { + Address = + MultiAddressHelper.GetAddress("publickey-3", IPAddress.Parse("172.0.0.3"), 9090), + Reputation = 2, + LastSeen = DateTime.UtcNow, + Created = DateTime.UtcNow + }; + } + + /// + /// Tests the get peer info request and response via RPC. + /// Peer is expected to be found in this case + /// + /// Public key of the peer whose reputation is of interest + /// Ip address of the peer whose reputation is of interest + [TestCase("publickey-1", "172.0.0.1")] + [TestCase("publickey-2", "172.0.0.2")] + public void TestGetPeerInfoRequestResponse(string publicKey, string ipAddress) + { + var peerId = MultiAddressHelper.GetAddress(publicKey, ipAddress, 9090); + var responseContent = GetPeerInfoTest(peerId); + responseContent.PeerInfo.Count().Should().Be(1); + + foreach (var peerInfo in responseContent.PeerInfo) + { + peerInfo.Address.ToString().Should().Be(peerId.ToString()); + } + } + + /// + /// Tests the get peer info request and response via RPC. + /// Peer is NOT expected to be found in this case, as they do not exist + /// + /// Public key of the peer whose reputation is of interest + /// Ip address of the peer whose reputation is of interest + [TestCase("this-pk-should-not-exist", "172.0.0.1")] + [TestCase("this-pk-should-not-exist", "172.0.0.3")] + [TestCase("publickey-1", "0.0.0.0")] + [TestCase("publickey-3", "0.0.0.0")] + public void TestGetPeerInfoRequestResponseForNonExistantPeers(string publicKey, string ipAddress) + { + var peerId = MultiAddressHelper.GetAddress(publicKey, ipAddress, 12345); + var responseContent = GetPeerInfoTest(peerId); + responseContent.PeerInfo.Count.Should().Be(0); + } + + /// + /// Tests the data/communication through protobuf + /// + /// + private GetPeerInfoResponse GetPeerInfoTest(MultiAddress address) + { + TestScheduler testScheduler = new(); + + var senderAddress = MultiAddressHelper.GetAddress("sender"); + var getPeerInfoRequest = new GetPeerInfoRequest {Address = address.ToString()}; + + var protocolMessage = getPeerInfoRequest.ToProtocolMessage(senderAddress); + + var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, protocolMessage); + + var peerSettings = senderAddress.ToSubstitutedPeerSettings(); + GetPeerInfoRequestObserver handler = new(peerSettings, _logger, _peerRepository); + + handler.StartObserving(messageStream); + + testScheduler.Start(); + + var receivedCalls = _fakeContext.Channel.ReceivedCalls().ToList(); + receivedCalls.Count.Should().Be(1); + + var sentResponseDto = (IMessageDto) receivedCalls[0].GetArguments().Single(); + + return sentResponseDto.Content.FromProtocolMessage(); + } + } +} diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetVersionRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetVersionRequestObserverTests.cs similarity index 81% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetVersionRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetVersionRequestObserverTests.cs index 0da9aa0001..b2d28f29ee 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/GetVersionRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/GetVersionRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,6 @@ using System.Linq; using System.Net; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; @@ -35,9 +34,10 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { public sealed class GetVersionRequestObserverTests { @@ -54,21 +54,21 @@ public GetVersionRequestObserverTests() _fakeContext.Channel.RemoteAddress.Returns(new IPEndPoint(IPAddress.Loopback, IPEndPoint.MaxPort)); } - [Fact] + [Test] public void Valid_GetVersion_Request_Should_Send_VersionResponse() { - var testScheduler = new TestScheduler(); + TestScheduler testScheduler = new(); - var versionRequest = new VersionRequest(); + VersionRequest versionRequest = new(); var protocolMessage = - versionRequest.ToProtocolMessage(PeerIdHelper.GetPeerId("sender")); + versionRequest.ToProtocolMessage(MultiAddressHelper.GetAddress("sender")); var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, protocolMessage ); - var peerSettings = PeerIdHelper.GetPeerId("sender").ToSubstitutedPeerSettings(); - var handler = new GetVersionRequestObserver(peerSettings, _logger); + var peerSettings = MultiAddressHelper.GetAddress("sender").ToSubstitutedPeerSettings(); + GetVersionRequestObserver handler = new(peerSettings, _logger); handler.StartObserving(messageStream); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerBlackListingRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerBlackListingRequestObserverTests.cs similarity index 55% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerBlackListingRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerBlackListingRequestObserverTests.cs index eff8a1901e..34ba5cba23 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerBlackListingRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerBlackListingRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,46 +22,45 @@ #endregion using System.Linq; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Network; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; -using Catalyst.Protocol.Peer; -using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; +using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Abstractions.P2P; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { /// - /// Tests the peer black listing calls + /// Tests the peer black listing calls /// public sealed class PeerBlackListingRequestObserverTests { - private readonly ILogger _logger; + private ILogger _logger; - private readonly IChannelHandlerContext _fakeContext; + private IChannelHandlerContext _fakeContext; - private readonly TestScheduler _testScheduler; - private readonly PeerId _senderId; - private readonly IPeerRepository _peerRepository; + private TestScheduler _testScheduler; + private MultiAddress _senderId; + private IPeerRepository _peerRepository; + private IPeerClient _peerClient; - public PeerBlackListingRequestObserverTests() + [SetUp] + public void Init() { _logger = Substitute.For(); _fakeContext = Substitute.For(); - - var fakeChannel = Substitute.For(); - _fakeContext.Channel.Returns(fakeChannel); - _fakeContext.Channel.RemoteAddress.Returns(EndpointBuilder.BuildNewEndPoint("192.0.0.1", 42042)); + _peerClient = Substitute.For(); _testScheduler = new TestScheduler(); _peerRepository = Substitute.For(); @@ -69,7 +68,7 @@ public PeerBlackListingRequestObserverTests() var fakePeers = PreparePeerRepositoryContent(); _peerRepository.GetAll().Returns(fakePeers); - _senderId = PeerIdHelper.GetPeerId("sender"); + _senderId = MultiAddressHelper.GetAddress("sender"); } private static Peer[] PreparePeerRepositoryContent() @@ -77,67 +76,64 @@ private static Peer[] PreparePeerRepositoryContent() var blacklistedPeers = Enumerable.Range(0, 5).Select(i => new Peer { Reputation = 0, - PeerId = PeerIdHelper.GetPeerId($"blacklisted-{i}"), + Address = MultiAddressHelper.GetAddress($"blacklisted-{i}"), BlackListed = true }); var goodPeers = Enumerable.Range(0, 23).Select(i => new Peer { Reputation = 125, - PeerId = PeerIdHelper.GetPeerId($"good-{i}") + Address = MultiAddressHelper.GetAddress($"good-{i}") }); var fakePeers = blacklistedPeers.Concat(goodPeers).ToArray(); return fakePeers; } - [Theory] - [InlineData("good-14", true)] - [InlineData("good-22", false)] - [InlineData("blacklisted-1", true)] - [InlineData("blacklisted-3", false)] - public void PeerBlackListingRequestObserver_should_set_Blacklist_flag_on_known_peers(string publicKeySeed, bool blacklist) + [TestCase("good-14", true)] + [TestCase("good-22", false)] + [TestCase("blacklisted-1", true)] + [TestCase("blacklisted-3", false)] + public void PeerBlackListingRequestObserver_should_set_Blacklist_flag_on_known_peers(string publicKeySeed, + bool blacklist) { - var targetedId = PeerIdHelper.GetPeerId(publicKeySeed); - var request = new SetPeerBlacklistRequest + var targetedId = MultiAddressHelper.GetAddress(publicKeySeed); + var request = new SetPeerBlackListRequest { - PublicKey = targetedId.PublicKey, - Ip = targetedId.Ip, + Address = targetedId.ToString(), Blacklist = blacklist }; var responseContent = GetSetPeerBlacklistRequest(request); responseContent.Blacklist.Should().Be(blacklist); - responseContent.Ip.Should().BeEquivalentTo(targetedId.Ip); - responseContent.PublicKey.Should().BeEquivalentTo(targetedId.PublicKey); + responseContent.Address.Should().Be(targetedId.ToString()); } - [Theory] - [InlineData("unknown-1", false)] - [InlineData("unknown-2", false)] - public void PeerBlackListingRequestObserver_should_not_set_Blacklist_flag_on_unknown_peers(string publicKeySeed, bool blacklist) + [TestCase("unknown-1", false)] + [TestCase("unknown-2", false)] + public void PeerBlackListingRequestObserver_should_not_set_Blacklist_flag_on_unknown_peers(string publicKeySeed, + bool blacklist) { - var targetedId = PeerIdHelper.GetPeerId(publicKeySeed); - var request = new SetPeerBlacklistRequest + var targetedId = MultiAddressHelper.GetAddress(publicKeySeed); + var request = new SetPeerBlackListRequest { - PublicKey = targetedId.PublicKey, - Ip = targetedId.Ip, + Address = targetedId.ToString(), Blacklist = blacklist }; var responseContent = GetSetPeerBlacklistRequest(request); - responseContent.Ip.Should().BeNullOrEmpty(); - responseContent.PublicKey.Should().BeNullOrEmpty(); + responseContent.Address.Should().BeNullOrEmpty(); } - private SetPeerBlacklistResponse GetSetPeerBlacklistRequest(SetPeerBlacklistRequest request) + private SetPeerBlackListResponse GetSetPeerBlacklistRequest(SetPeerBlackListRequest request) { var protocolMessage = request.ToProtocolMessage(_senderId); - var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, _testScheduler, protocolMessage); + var messageStream = + MessageStreamHelper.CreateStreamWithMessage(_fakeContext, _testScheduler, protocolMessage); var peerSettings = _senderId.ToSubstitutedPeerSettings(); - var handler = new PeerBlackListingRequestObserver(peerSettings, _logger, _peerRepository); + PeerBlackListingRequestObserver handler = new(peerSettings, _logger, _peerRepository); handler.StartObserving(messageStream); _testScheduler.Start(); @@ -147,7 +143,7 @@ private SetPeerBlacklistResponse GetSetPeerBlacklistRequest(SetPeerBlacklistRequ var sentResponseDto = (IMessageDto) receivedCalls.Single().GetArguments().Single(); - return sentResponseDto.Content.FromProtocolMessage(); + return sentResponseDto.Content.FromProtocolMessage(); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerCountRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerCountRequestObserverTests.cs similarity index 78% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerCountRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerCountRequestObserverTests.cs index 03f10fdd61..0f178cc85f 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerCountRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerCountRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,36 +24,36 @@ using System; using System.Collections.Generic; using System.Linq; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Network; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; -using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; +using Catalyst.Protocol.Wire; using Catalyst.TestUtils; -using DotNetty.Transport.Channels; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Core.Lib.Network; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { /// /// Tests the peer count CLI and RPC calls /// public sealed class PeerCountRequestObserverTests { - private readonly TestScheduler _testScheduler; + private TestScheduler _testScheduler; /// The logger - private readonly ILogger _logger; + private ILogger _logger; /// The fake channel context - private readonly IChannelHandlerContext _fakeContext; + private IChannelHandlerContext _fakeContext; /// /// Initializes a new instance of the @@ -62,10 +62,12 @@ public sealed class PeerCountRequestObserverTests /// /// class. /// - public PeerCountRequestObserverTests() + [SetUp] + public void Init() { _testScheduler = new TestScheduler(); _logger = Substitute.For(); + _fakeContext = Substitute.For(); var fakeChannel = Substitute.For(); _fakeContext.Channel.Returns(fakeChannel); @@ -75,13 +77,12 @@ public PeerCountRequestObserverTests() /// Tests the peer count request and response. /// /// The peer count. - [Theory] - [InlineData(40)] - [InlineData(20)] + [TestCase(40)] + [TestCase(20)] public void TestPeerListRequestResponse(int fakePeers) { - var peerRepository = Substitute.For(); - var peerList = new List(); + var peerService = Substitute.For(); + List peerList = new(); for (var i = 0; i < fakePeers; i++) { @@ -89,24 +90,24 @@ public void TestPeerListRequestResponse(int fakePeers) { Reputation = 0, LastSeen = DateTime.Now, - PeerId = PeerIdHelper.GetPeerId(i.ToString()) + Address = MultiAddressHelper.GetAddress(i.ToString()) }); } // Build a fake remote endpoint _fakeContext.Channel.RemoteAddress.Returns(EndpointBuilder.BuildNewEndPoint("192.0.0.1", 42042)); - peerRepository.GetAll().Returns(peerList); + peerService.GetAll().Returns(peerList); - var sendPeerId = PeerIdHelper.GetPeerId("sender"); + var sendPeerId = MultiAddressHelper.GetAddress("sender"); var protocolMessage = - new GetPeerCountRequest().ToProtocolMessage(PeerIdHelper.GetPeerId("sender")); + new GetPeerCountRequest().ToProtocolMessage(MultiAddressHelper.GetAddress("sender")); var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, _testScheduler, protocolMessage); var peerSettings = sendPeerId.ToSubstitutedPeerSettings(); - var handler = new PeerCountRequestObserver(peerSettings, peerRepository, _logger); + PeerCountRequestObserver handler = new(peerSettings, peerService, _logger); handler.StartObserving(messageStream); _testScheduler.Start(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerListRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerListRequestObserverTests.cs similarity index 66% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerListRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerListRequestObserverTests.cs index d18e970d0e..5ffaf51c36 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerListRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerListRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,61 +24,63 @@ using System; using System.Collections.Generic; using System.Linq; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Network; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; -using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; +using Catalyst.Protocol.Wire; using Catalyst.TestUtils; -using DotNetty.Transport.Channels; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Core.Lib.Network; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { /// - /// Tests the peer list CLI and RPC calls + /// Tests the peer list CLI and RPC calls /// public sealed class PeerListRequestObserverTests { /// The logger - private readonly ILogger _logger; + private ILogger _logger; /// The fake channel context - private readonly IChannelHandlerContext _fakeContext; + private IChannelHandlerContext _fakeContext; /// - /// Initializes a new instance of the - /// PeerListRequestObserverTest - /// - /// class. + /// Initializes a new instance of the + /// + /// PeerListRequestObserverTest + /// + /// class. /// - public PeerListRequestObserverTests() + [SetUp] + public void Init() { _logger = Substitute.For(); + _fakeContext = Substitute.For(); var fakeChannel = Substitute.For(); _fakeContext.Channel.Returns(fakeChannel); } /// - /// Tests the peer list request and response. + /// Tests the peer list request and response. /// /// The fake peers. - [Theory] - [InlineData("FakePeer1", "FakePeer2")] - [InlineData("FakePeer1002", "FakePeer6000", "FakePeerSataoshi")] + [TestCase("FakePeer1", "FakePeer2")] + [TestCase("FakePeer1002", "FakePeer6000", "FakePeerSataoshi")] public void TestPeerListRequestResponse(params string[] fakePeers) { - var testScheduler = new TestScheduler(); - var peerRepository = Substitute.For(); - var peerList = new List(); + TestScheduler testScheduler = new(); + var peerService = Substitute.For(); + List peerList = new(); fakePeers.ToList().ForEach(fakePeer => { @@ -86,21 +88,22 @@ public void TestPeerListRequestResponse(params string[] fakePeers) { Reputation = 0, LastSeen = DateTime.Now, - PeerId = PeerIdHelper.GetPeerId(fakePeer) + Address = MultiAddressHelper.GetAddress(fakePeer) }); }); // Let peerRepository return the fake peer list - peerRepository.GetAll().Returns(peerList.ToArray()); + peerService.GetAll().Returns(peerList.ToArray()); // Build a fake remote endpoint _fakeContext.Channel.RemoteAddress.Returns(EndpointBuilder.BuildNewEndPoint("192.0.0.1", 42042)); - - var protocolMessage = new GetPeerListRequest().ToProtocolMessage(PeerIdHelper.GetPeerId("sender")); - var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, protocolMessage); - var peerSettings = PeerIdHelper.GetPeerId("sender").ToSubstitutedPeerSettings(); - var handler = new PeerListRequestObserver(peerSettings, _logger, peerRepository); + var protocolMessage = new GetPeerListRequest().ToProtocolMessage(MultiAddressHelper.GetAddress("sender")); + var messageStream = + MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, protocolMessage); + + var peerSettings = MultiAddressHelper.GetAddress("sender").ToSubstitutedPeerSettings(); + PeerListRequestObserver handler = new(peerSettings, _logger, peerService); handler.StartObserving(messageStream); testScheduler.Start(); @@ -109,7 +112,7 @@ public void TestPeerListRequestResponse(params string[] fakePeers) receivedCalls.Count.Should().Be(1); var sentResponseDto = (IMessageDto) receivedCalls[0].GetArguments().Single(); - + var responseContent = sentResponseDto.Content.FromProtocolMessage(); responseContent.Peers.Count.Should().Be(fakePeers.Length); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerReputationRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerReputationRequestObserverTests.cs similarity index 72% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerReputationRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerReputationRequestObserverTests.cs index c89ea582d9..8b95489c52 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/PeerReputationRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/PeerReputationRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,34 +22,35 @@ #endregion using System.Linq; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Network; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; -using Catalyst.Protocol.Peer; -using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; +using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using DotNetty.Transport.Channels; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Core.Lib.Network; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { public sealed class PeerReputationRequestObserverTests { - private readonly ILogger _logger; - private readonly IChannelHandlerContext _fakeContext; - private readonly TestScheduler _testScheduler; - private readonly IPeerRepository _peerRepository; - private readonly PeerId _senderId; - - public PeerReputationRequestObserverTests() + private ILogger _logger; + private IChannelHandlerContext _fakeContext; + private TestScheduler _testScheduler; + private IPeerRepository _peerRepository; + private MultiAddress _senderId; + + [SetUp] + public void Init() { _logger = Substitute.For(); _fakeContext = Substitute.For(); @@ -64,7 +65,7 @@ public PeerReputationRequestObserverTests() var fakePeers = PreparePeerRepositoryContent(); _peerRepository.GetAll().Returns(fakePeers); - _senderId = PeerIdHelper.GetPeerId("sender"); + _senderId = MultiAddressHelper.GetAddress("sender"); } private static Peer[] PreparePeerRepositoryContent() @@ -72,22 +73,21 @@ private static Peer[] PreparePeerRepositoryContent() var knownPeers = Enumerable.Range(0, 5).Select(i => new Peer { Reputation = i, - PeerId = PeerIdHelper.GetPeerId($"peer-{i}") + Address = MultiAddressHelper.GetAddress($"peer-{i}") }); var fakePeers = knownPeers.ToArray(); return fakePeers; } - [Theory] - [InlineData("peer-1", 1)] - [InlineData("peer-4", 4)] - [InlineData("unknown", int.MinValue)] + [TestCase("peer-1", 1)] + [TestCase("peer-4", 4)] + [TestCase("unknown", int.MinValue)] public void TestPeerReputationRequestResponse(string publicKeySeed, int expectedReputations) { - var peerId = PeerIdHelper.GetPeerId(publicKeySeed); + var peerId = MultiAddressHelper.GetAddress(publicKeySeed); - var request = new GetPeerReputationRequest {Ip = peerId.Ip, PublicKey = peerId.PublicKey}; + var request = new GetPeerReputationRequest { Address = peerId.ToString() }; var responseContent = GetGetPeerReputationResponse(request); @@ -97,10 +97,11 @@ public void TestPeerReputationRequestResponse(string publicKeySeed, int expected private GetPeerReputationResponse GetGetPeerReputationResponse(GetPeerReputationRequest request) { var protocolMessage = request.ToProtocolMessage(_senderId); - var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, _testScheduler, protocolMessage); + var messageStream = + MessageStreamHelper.CreateStreamWithMessage(_fakeContext, _testScheduler, protocolMessage); var peerSettings = _senderId.ToSubstitutedPeerSettings(); - var handler = new PeerReputationRequestObserver(peerSettings, _logger, _peerRepository); + PeerReputationRequestObserver handler = new(peerSettings, _logger, _peerRepository); handler.StartObserving(messageStream); _testScheduler.Start(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/RemovePeerRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/RemovePeerRequestObserverTests.cs similarity index 60% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/RemovePeerRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/RemovePeerRequestObserverTests.cs index f6feab4641..fe3cdf3730 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/RemovePeerRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/RemovePeerRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,67 +24,67 @@ using System; using System.Collections.Generic; using System.Linq; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Network; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; -using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; +using Catalyst.Protocol.Wire; using Catalyst.TestUtils; -using DotNetty.Transport.Channels; using FluentAssertions; -using Google.Protobuf; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using SharpRepository.Repository.Specifications; -using Xunit; +using SharpRepository.InMemoryRepository; +using NUnit.Framework; +using Catalyst.Core.Lib.P2P.Repository; +using DotNetty.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { /// - /// Tests remove peer CLI and RPC calls + /// Tests remove peer CLI and RPC calls /// public sealed class RemovePeerRequestObserverTests { /// The logger - private readonly ILogger _logger; + private ILogger _logger; /// The fake channel context - private readonly IChannelHandlerContext _fakeContext; + private IChannelHandlerContext _fakeContext; /// - /// Initializes a new instance of the - /// RemovePeerRequestObserverTest - /// - /// class. + /// Initializes a new instance of the + /// + /// RemovePeerRequestObserverTest + /// + /// class. /// - public RemovePeerRequestObserverTests() + [SetUp] + public void Init() { _logger = Substitute.For(); + _fakeContext = Substitute.For(); var fakeChannel = Substitute.For(); _fakeContext.Channel.Returns(fakeChannel); } /// - /// Tests the peer list request and response. + /// Tests the peer list request and response. /// /// The fake peers. - [Theory] - [InlineData("FakePeer1", "FakePeer2")] - [InlineData("FakePeer1002", "FakePeer6000", "FakePeerSataoshi")] + [TestCase("FakePeer1", "FakePeer2")] + [TestCase("FakePeer1002", "FakePeer6000", "FakePeerSataoshi")] public void TestRemovePeer(params string[] fakePeers) { ExecuteTestCase(fakePeers, true); } /// - /// Tests peer removal via IP only. + /// Tests peer removal via IP only. /// /// The fake peers. - [Theory] - [InlineData("Fake1Peer1", "Fake2Peer2")] - [InlineData("Fake1Peer1002", "Fake2Peer6000", "FakePeer3Sataoshi")] + [TestCase("Fake1Peer1", "Fake2Peer2")] + [TestCase("Fake1Peer1002", "Fake2Peer6000", "FakePeer3Sataoshi")] public void TestRemovePeerWithoutPublicKey(params string[] fakePeers) { ExecuteTestCase(fakePeers, false); } /// Executes the test case. @@ -92,45 +92,36 @@ public RemovePeerRequestObserverTests() /// if set to true [send message to handler with the public key]. private void ExecuteTestCase(IReadOnlyCollection fakePeers, bool withPublicKey) { - var testScheduler = new TestScheduler(); - IPeerRepository peerRepository = Substitute.For(); - Peer targetPeerToDelete = null; + TestScheduler testScheduler = new(); + IPeerRepository peerRepository = new PeerRepository(new InMemoryRepository()); var fakePeerList = fakePeers.ToList().Select(fakePeer => { - var peer = new Peer + return new Peer { Reputation = 0, LastSeen = DateTime.Now.Subtract(TimeSpan.FromSeconds(fakePeers.ToList().IndexOf(fakePeer))), - PeerId = PeerIdHelper.GetPeerId(fakePeer) + Address = MultiAddressHelper.GetAddress(fakePeer) }; - - if (targetPeerToDelete == null) - { - targetPeerToDelete = peer; - } - - return peer; }).ToList(); - peerRepository.FindAll(Arg.Any>()).Returns(withPublicKey ? new List {targetPeerToDelete} : fakePeerList); - - // Build a fake remote endpoint - _fakeContext.Channel.RemoteAddress.Returns(EndpointBuilder.BuildNewEndPoint("192.0.0.1", 42042)); + var targetPeerToDelete = fakePeerList[0]; + + peerRepository.Add(fakePeerList); - var peerId = PeerIdHelper.GetPeerId("sender"); + var peerId = MultiAddressHelper.GetAddress("sender"); var removePeerRequest = new RemovePeerRequest { - PeerIp = targetPeerToDelete.PeerId.Ip, - PublicKey = withPublicKey ? targetPeerToDelete.PeerId.PublicKey : ByteString.Empty + Address = targetPeerToDelete.Address.ToString() }; var protocolMessage = removePeerRequest.ToProtocolMessage(peerId); - var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, protocolMessage); + var messageStream = + MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, protocolMessage); var peerSettings = peerId.ToSubstitutedPeerSettings(); - var handler = new RemovePeerRequestObserver(peerSettings, peerRepository, _logger); + RemovePeerRequestObserver handler = new(peerSettings, peerRepository, _logger); handler.StartObserving(messageStream); testScheduler.Start(); @@ -142,7 +133,7 @@ private void ExecuteTestCase(IReadOnlyCollection fakePeers, bool withPub var signResponseMessage = sentResponseDto.Content.FromProtocolMessage(); - signResponseMessage.DeletedCount.Should().Be(withPublicKey ? 1 : (uint) fakePeers.Count); + signResponseMessage.DeletedCount.Should().Be(1); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/SignMessageRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/SignMessageRequestObserverTests.cs similarity index 74% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/SignMessageRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/SignMessageRequestObserverTests.cs index 1a57abf648..ccc4f540c0 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/SignMessageRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/SignMessageRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,6 @@ using System.Linq; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.KeySigner; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Util; @@ -33,25 +32,29 @@ using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; using Catalyst.TestUtils; -using DotNetty.Transport.Channels; +using Catalyst.TestUtils.Fakes; using FluentAssertions; using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Google.Protobuf; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { public sealed class SignMessageRequestObserverTests { - private readonly ILogger _logger; - private readonly IKeySigner _keySigner; - private readonly IChannelHandlerContext _fakeContext; - private readonly ISignature _signature; + private ILogger _logger; + private IKeySigner _keySigner; + private IChannelHandlerContext _fakeContext; + private ISignature _signature; - public SignMessageRequestObserverTests() + [SetUp] + public void Init() { - _keySigner = Substitute.For(); + _keySigner = Substitute.For(); _signature = Substitute.For(); _signature.SignatureBytes.Returns(ByteUtil.GenerateRandomByteArray(new FfiWrapper().SignatureLength)); _signature.PublicKeyBytes.Returns(ByteUtil.GenerateRandomByteArray(new FfiWrapper().PublicKeyLength)); @@ -63,13 +66,12 @@ public SignMessageRequestObserverTests() _keySigner.Sign(default, default).ReturnsForAnyArgs(_signature); } - [Theory] - [InlineData("Hello Catalyst")] - [InlineData("")] - [InlineData("Hello&?!1253Catalyst")] + [TestCase("Hello Catalyst")] + [TestCase("")] + [TestCase("Hello&?!1253Catalyst")] public void SignMessageRequestObserver_Can_Return_SignMessageResponse(string message) { - var testScheduler = new TestScheduler(); + TestScheduler testScheduler = new(); var signMessageRequest = new SignMessageRequest { @@ -78,13 +80,13 @@ public void SignMessageRequestObserver_Can_Return_SignMessageResponse(string mes }; var protocolMessage = - signMessageRequest.ToProtocolMessage(PeerIdHelper.GetPeerId("sender")); + signMessageRequest.ToProtocolMessage(MultiAddressHelper.GetAddress("sender")); var messageStream = MessageStreamHelper.CreateStreamWithMessage(_fakeContext, testScheduler, protocolMessage); - var peerSettings = PeerIdHelper.GetPeerId("sender").ToSubstitutedPeerSettings(); - var handler = - new SignMessageRequestObserver(peerSettings, _logger, _keySigner); + var peerSettings = MultiAddressHelper.GetAddress("sender").ToSubstitutedPeerSettings(); + SignMessageRequestObserver handler = + new(peerSettings, _logger, _keySigner); handler.StartObserving(messageStream); @@ -96,7 +98,7 @@ public void SignMessageRequestObserver_Can_Return_SignMessageResponse(string mes var sentResponseDto = (IMessageDto) receivedCalls.Single().GetArguments().Single(); var signResponseMessage = sentResponseDto.Content.FromProtocolMessage(); - signResponseMessage.OriginalMessage.Should().Equal(message); + signResponseMessage.OriginalMessage.Should().Equal(ByteString.CopyFromUtf8(message)); signResponseMessage.Signature.ToByteArray().Should().Equal(_signature.SignatureBytes); signResponseMessage.PublicKey.ToByteArray().Should().Equal(_signature.PublicKeyBytes); } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/TransferFileBytesRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/TransferFileBytesRequestObserverTests.cs similarity index 75% rename from src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/TransferFileBytesRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/TransferFileBytesRequestObserverTests.cs index b5f3ba6354..bd1cbaf32c 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/IO/Observers/TransferFileBytesRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/TransferFileBytesRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,12 +22,9 @@ #endregion using System.Linq; -using Catalyst.Abstractions.FileTransfer; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Core.Modules.Rpc.Server.IO.Observers; using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; @@ -38,28 +35,32 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; -namespace Catalyst.Core.Lib.Tests.UnitTests.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { public sealed class TransferFileBytesRequestObserverTests { - private readonly TransferFileBytesRequestObserver _observer; - private readonly IDownloadFileTransferFactory _downloadFileTransferFactory; - private readonly IChannelHandlerContext _context; + private TransferFileBytesRequestObserver _observer; + private IDownloadFileTransferFactory _downloadFileTransferFactory; + private IChannelHandlerContext _context; - public TransferFileBytesRequestObserverTests() + [SetUp] + public void Init() { _context = Substitute.For(); _context.Channel.Returns(Substitute.For()); _downloadFileTransferFactory = Substitute.For(); - var peerSettings = PeerIdHelper.GetPeerId("Test").ToSubstitutedPeerSettings(); + var peerSettings = MultiAddressHelper.GetAddress("Test").ToSubstitutedPeerSettings(); _observer = new TransferFileBytesRequestObserver(_downloadFileTransferFactory, peerSettings, Substitute.For()); } - [Fact] + [Test] public void CanHandlerDownloadChunk() { var guid = CorrelationId.GenerateCorrelationId(); @@ -68,7 +69,7 @@ public void CanHandlerDownloadChunk() ChunkBytes = ByteString.Empty, ChunkId = 1, CorrelationFileName = CorrelationId.GenerateCorrelationId().Id.ToByteString() - }.ToProtocolMessage(PeerIdHelper.GetPeerId("Test"), guid); + }.ToProtocolMessage(MultiAddressHelper.GetAddress("Test"), guid); _downloadFileTransferFactory.DownloadChunk(Arg.Any()) .Returns(FileTransferResponseCodeTypes.Successful); @@ -77,16 +78,16 @@ public void CanHandlerDownloadChunk() _downloadFileTransferFactory.Received(1).DownloadChunk(Arg.Any()); } - [Fact] + [Test] public void HandlerCanSendErrorOnException() { - var testScheduler = new TestScheduler(); + TestScheduler testScheduler = new(); _downloadFileTransferFactory.DownloadChunk(Arg.Any()).Returns(FileTransferResponseCodeTypes.Error); - var sender = PeerIdHelper.GetPeerId("sender"); - var requestDto = new MessageDto(new TransferFileBytesRequest().ToProtocolMessage(sender) - , PeerIdHelper.GetPeerId("recipient")); + var sender = MultiAddressHelper.GetAddress("sender"); + MessageDto requestDto = new(new TransferFileBytesRequest().ToProtocolMessage(sender) + , MultiAddressHelper.GetAddress("recipient")); var messageStream = MessageStreamHelper.CreateStreamWithMessage(_context, testScheduler, requestDto.Content); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/VerifyMessageRequestObserverTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/VerifyMessageRequestObserverTests.cs similarity index 84% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/VerifyMessageRequestObserverTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/VerifyMessageRequestObserverTests.cs index 13c0f03e13..8b4616c19a 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Observers/VerifyMessageRequestObserverTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Observers/VerifyMessageRequestObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -33,24 +33,26 @@ using NSubstitute; using Serilog; using System.Linq; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Network; -using Catalyst.Protocol.Peer; -using Xunit; +using Catalyst.TestUtils.Fakes; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using MultiFormats; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Observers +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Observers { public sealed class VerifyMessageRequestObserverTests { - private readonly IKeySigner _keySigner; - private readonly VerifyMessageRequestObserver _verifyMessageRequestObserver; - private readonly IChannelHandlerContext _fakeContext; - private readonly PeerId _testPeerId; - private readonly VerifyMessageRequest _verifyMessageRequest; - private readonly SigningContext _signingContext; - - public VerifyMessageRequestObserverTests() + private IKeySigner _keySigner; + private VerifyMessageRequestObserver _verifyMessageRequestObserver; + private IChannelHandlerContext _fakeContext; + private MultiAddress _testPeerId; + private VerifyMessageRequest _verifyMessageRequest; + private SigningContext _signingContext; + + [SetUp] + public void Init() { _signingContext = new SigningContext { @@ -58,11 +60,11 @@ public VerifyMessageRequestObserverTests() SignatureType = SignatureType.ProtocolRpc }; - _testPeerId = PeerIdHelper.GetPeerId("TestPeerIdentifier"); + _testPeerId = MultiAddressHelper.GetAddress("TestPeerIdentifier"); var peerSettings = _testPeerId.ToSubstitutedPeerSettings(); - _keySigner = Substitute.For(); + _keySigner = Substitute.For(); _keySigner.CryptoContext.Returns(new FfiWrapper()); var logger = Substitute.For(); @@ -75,7 +77,7 @@ public VerifyMessageRequestObserverTests() _verifyMessageRequest = GetValidVerifyMessageRequest(); } - [Fact] + [Test] public void VerifyMessageRequestObserver_Can_Reject_Invalid_Public_Key_Length() { _verifyMessageRequest.PublicKey = ByteString.CopyFrom(new byte[new FfiWrapper().PublicKeyLength + 1]); @@ -83,21 +85,21 @@ public void VerifyMessageRequestObserver_Can_Reject_Invalid_Public_Key_Length() AssertVerifyResponse(false); } - [Fact] + [Test] public void VerifyMessageRequestObserver_Can_Reject_Invalid_Signature_Length() { _verifyMessageRequest.Signature = ByteString.CopyFrom(new byte[new FfiWrapper().SignatureLength + 1]); AssertVerifyResponse(false); } - [Fact] + [Test] public void VerifyMessageRequestObserver_Can_Send_True_If_Valid_Signature() { _keySigner.Verify(default, default, default).ReturnsForAnyArgs(true); AssertVerifyResponse(true); } - [Fact] + [Test] public void VerifyMessageRequestObserver_Can_Send_False_Response_If_Verify_Fails() { _keySigner.Verify(default, default, default).ReturnsForAnyArgs(false); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Transport/Channels/RpcClientChannelFactoryTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Transport/Channels/RpcClientChannelFactoryTests.cs similarity index 80% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Transport/Channels/RpcClientChannelFactoryTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Transport/Channels/RpcClientChannelFactoryTests.cs index 29d4502534..1b06f374ae 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Transport/Channels/RpcClientChannelFactoryTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Transport/Channels/RpcClientChannelFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,8 +28,6 @@ using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Rpc.IO.Messaging.Correlation; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Codecs; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Cryptography.BulletProofs; @@ -37,8 +35,8 @@ using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.IPPN; using Catalyst.Protocol.Network; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using DotNetty.Buffers; using DotNetty.Codecs.Protobuf; using DotNetty.Transport.Channels; @@ -47,9 +45,14 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; - -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Transport.Channels +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.IO.Codecs; +using Catalyst.Protocol.Wire; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; + +namespace Catalyst.Core.Modules.Rpc.Client.Tests.UnitTests.IO.Transport.Channels { public sealed class RpcClientChannelFactoryTests { @@ -70,19 +73,20 @@ public TestRpcClientChannelFactory(IKeySigner keySigner, public IReadOnlyCollection InheritedHandlers => _handlers; } - private readonly TestScheduler _testScheduler; - private readonly IRpcMessageCorrelationManager _correlationManager; - private readonly TestRpcClientChannelFactory _factory; - private readonly IKeySigner _keySigner; + private TestScheduler _testScheduler; + private IRpcMessageCorrelationManager _correlationManager; + private TestRpcClientChannelFactory _factory; + private FakeKeySigner _keySigner; - public RpcClientChannelFactoryTests() + [SetUp] + public void Init() { _testScheduler = new TestScheduler(); _correlationManager = Substitute.For(); - _keySigner = Substitute.For(); + _keySigner = Substitute.For(); var peerIdValidator = Substitute.For(); - peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); + peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); var peerSettings = Substitute.For(); peerSettings.NetworkType.Returns(NetworkType.Devnet); @@ -91,7 +95,7 @@ public RpcClientChannelFactoryTests() peerSettings, _testScheduler); } - [Fact] + [Test] public void RpcClientChannelFactory_should_have_correct_handlers() { _factory.InheritedHandlers.Count(h => h != null).Should().Be(10); @@ -105,16 +109,16 @@ public void RpcClientChannelFactory_should_have_correct_handlers() handlers[6].Should().BeOfType(); handlers[7].Should().BeOfType>(); handlers[8].Should().BeOfType>(); - handlers[9].Should().BeOfType(); + handlers[9].Should().BeOfType(); } - [Fact] + [Test] public void RpcClientChannelFactory_should_put_the_correct_inbound_handlers_on_the_pipeline() { - var testingChannel = new EmbeddedChannel("test".ToChannelId(), + EmbeddedChannel testingChannel = new("test".ToChannelId(), true, _factory.InheritedHandlers.ToArray()); - var senderId = PeerIdHelper.GetPeerId("sender"); + var senderId = MultiAddressHelper.GetAddress("sender"); var correlationId = CorrelationId.GenerateCorrelationId(); var signatureBytes = ByteUtil.GenerateRandomByteArray(new FfiWrapper().SignatureLength); @@ -126,9 +130,9 @@ public void RpcClientChannelFactory_should_put_the_correct_inbound_handlers_on_t _correlationManager.TryMatchResponse(protocolMessage).Returns(true); - var observer = new ProtocolMessageObserver(0, Substitute.For()); + ProtocolMessageObserver> observer = new(0, Substitute.For()); - var messageStream = _factory.InheritedHandlers.OfType().Single().MessageStream; + var messageStream = _factory.InheritedHandlers.OfType().Single().MessageStream; using (messageStream.Subscribe(observer)) { @@ -145,13 +149,13 @@ public void RpcClientChannelFactory_should_put_the_correct_inbound_handlers_on_t } } - [Fact] + [Test] public void RpcClientChannelFactory_should_put_the_correct_outbound_handlers_on_the_pipeline() { - var testingChannel = new EmbeddedChannel("test".ToChannelId(), + EmbeddedChannel testingChannel = new("test".ToChannelId(), true, _factory.InheritedHandlers.ToArray()); - var senderId = PeerIdHelper.GetPeerId("sender"); + var senderId = MultiAddressHelper.GetAddress("sender"); var correlationId = CorrelationId.GenerateCorrelationId(); var protocolMessage = new PingRequest().ToProtocolMessage(senderId, correlationId); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Transport/Channels/RpcServerChannelFactoryTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Transport/Channels/RpcServerChannelFactoryTests.cs similarity index 81% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Transport/Channels/RpcServerChannelFactoryTests.cs rename to src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Transport/Channels/RpcServerChannelFactoryTests.cs index 2e1c075d6a..44f408bce7 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Transport/Channels/RpcServerChannelFactoryTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/IO/Transport/Channels/RpcServerChannelFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,17 +29,14 @@ using Catalyst.Abstractions.Rpc.Authentication; using Catalyst.Abstractions.Rpc.IO.Messaging.Correlation; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Codecs; -using Catalyst.Core.Lib.IO.Handlers; using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.Core.Modules.Rpc.Server.Transport.Channels; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Network; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; +using Catalyst.TestUtils.Fakes; using DotNetty.Buffers; using DotNetty.Codecs.Protobuf; using DotNetty.Transport.Channels; @@ -48,9 +45,15 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Protocol.Network; +using Catalyst.Modules.Network.Dotnetty.IO.Codecs; +using Catalyst.Protocol.Wire; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Transport.Channels +namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests.IO.Transport.Channels { public sealed class RpcServerChannelFactoryTests { @@ -72,26 +75,27 @@ public TestRpcServerChannelFactory(IRpcMessageCorrelationManager correlationMana public IReadOnlyCollection InheritedHandlers => _handlers; } - private readonly TestScheduler _testScheduler; - private readonly IRpcMessageCorrelationManager _correlationManager; - private readonly TestRpcServerChannelFactory _factory; - private readonly IKeySigner _keySigner; + private TestScheduler _testScheduler; + private IRpcMessageCorrelationManager _correlationManager; + private TestRpcServerChannelFactory _factory; + private FakeKeySigner _keySigner; - public RpcServerChannelFactoryTests() + [SetUp] + public void Init() { _testScheduler = new TestScheduler(); _correlationManager = Substitute.For(); - _keySigner = Substitute.For(); + _keySigner = Substitute.For(); _keySigner.CryptoContext.SignatureLength.Returns(64); var authenticationStrategy = Substitute.For(); - authenticationStrategy.Authenticate(Arg.Any()).Returns(true); + authenticationStrategy.Authenticate(Arg.Any()).Returns(true); var peerSettings = Substitute.For(); peerSettings.NetworkType.Returns(NetworkType.Devnet); var peerIdValidator = Substitute.For(); - peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); + peerIdValidator.ValidatePeerIdFormat(Arg.Any()).Returns(true); _factory = new TestRpcServerChannelFactory( _correlationManager, _keySigner, @@ -101,7 +105,7 @@ public RpcServerChannelFactoryTests() _testScheduler); } - [Fact] + [Test] public void RpcServerChannelFactory_should_have_correct_handlers() { _factory.InheritedHandlers.Count(h => h != null).Should().Be(10); @@ -115,25 +119,25 @@ public void RpcServerChannelFactory_should_have_correct_handlers() handlers[6].Should().BeOfType(); handlers[7].Should().BeOfType>(); handlers[8].Should().BeOfType>(); - handlers[9].Should().BeOfType(); + handlers[9].Should().BeOfType(); } - [Fact] + [Test] public void RpcServerChannelFactory_should_put_the_correct_inbound_handlers_on_the_pipeline() { - var testingChannel = new EmbeddedChannel("test".ToChannelId(), + EmbeddedChannel testingChannel = new("test".ToChannelId(), true, _factory.InheritedHandlers.ToArray()); - var senderId = PeerIdHelper.GetPeerId("sender"); + var senderId = MultiAddressHelper.GetAddress("sender"); var correlationId = CorrelationId.GenerateCorrelationId(); var signatureBytes = ByteUtil.GenerateRandomByteArray(new FfiWrapper().SignatureLength); _keySigner.Verify(Arg.Any(), Arg.Any(), Arg.Any()) .Returns(true); var protocolMessage = new PingRequest().ToSignedProtocolMessage(senderId, signatureBytes, correlationId: correlationId); - var observer = new ProtocolMessageObserver(0, Substitute.For()); + ProtocolMessageObserver> observer = new(0, Substitute.For()); - var messageStream = _factory.InheritedHandlers.OfType() + var messageStream = _factory.InheritedHandlers.OfType() .Single().MessageStream; using (messageStream.Subscribe(observer)) @@ -147,13 +151,13 @@ public void RpcServerChannelFactory_should_put_the_correct_inbound_handlers_on_t } } - [Fact] + [Test] public void RpcServerChannelFactory_should_put_the_correct_outbound_handlers_on_the_pipeline() { - var testingChannel = new EmbeddedChannel("test".ToChannelId(), + EmbeddedChannel testingChannel = new("test".ToChannelId(), true, _factory.InheritedHandlers.ToArray()); - var senderId = PeerIdHelper.GetPeerId("sender"); + var senderId = MultiAddressHelper.GetAddress("sender"); var correlationId = CorrelationId.GenerateCorrelationId(); var protocolMessage = new PingResponse().ToProtocolMessage(senderId, correlationId); diff --git a/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/NodeRpcServerTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/NodeRpcServerTests.cs index 16fe47e34c..4baa0b7a3e 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/NodeRpcServerTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/NodeRpcServerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,21 +23,14 @@ using System; using System.Collections.Generic; -using System.Net; using System.Reactive.Linq; using System.Reactive.Subjects; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.Rpc; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; using Catalyst.TestUtils; @@ -46,7 +39,13 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests { @@ -57,19 +56,19 @@ public RpcServerTests() var logger = Substitute.For(); _testScheduler = new TestScheduler(); _rpcServerSettings = Substitute.For(); - _peerId = PeerIdHelper.GetPeerId(nameof(RpcServerTests)); + _peerId = MultiAddressHelper.GetAddress(nameof(RpcServerTests)); _channelHandlerContext = Substitute.For(); _mockSocketReplySubject = new ReplaySubject>(1, _testScheduler); var tcpServerEventLoopGroupFactory = Substitute.For(); - var tcpServerChannelFactory = Substitute.For(); - tcpServerChannelFactory.BuildChannelAsync(tcpServerEventLoopGroupFactory, Arg.Any(), Arg.Any(), - Arg.Any()).Returns(ObservableHelpers.MockObservableChannel(_mockSocketReplySubject)); + var tcpServerChannelFactory = Substitute.For>>(); + tcpServerChannelFactory.BuildChannelAsync(tcpServerEventLoopGroupFactory, Arg.Any(), + Arg.Any()).Returns(ObservableHelpers.MockRpcObservableChannel(_mockSocketReplySubject)); var certificateStore = Substitute.For(); // ReSharper disable once CollectionNeverUpdated.Local - var requestHandlers = new List(); + List requestHandlers = new(); _rpcServer = new RpcServer(_rpcServerSettings, logger, tcpServerChannelFactory, certificateStore, requestHandlers, tcpServerEventLoopGroupFactory); @@ -77,24 +76,24 @@ public RpcServerTests() private readonly ReplaySubject> _mockSocketReplySubject; private readonly TestScheduler _testScheduler; - private readonly PeerId _peerId; + private readonly MultiAddress _peerId; private readonly RpcServer _rpcServer; private readonly IRpcServerSettings _rpcServerSettings; private readonly IChannelHandlerContext _channelHandlerContext; - [Fact] + [Test] public void Get_Settings_From_RpcServer_Should_Return_Settings() { _rpcServer.Settings.Should().Be(_rpcServerSettings); } - [Fact] + [Test] public async Task Subscribe_To_Message_Stream_Should_Return_VersionRequest() { await _rpcServer.StartAsync(); VersionRequest returnedVersionRequest = null; - var targetVersionRequest = new VersionRequest {Query = true}; + var targetVersionRequest = new VersionRequest { Query = true }; var protocolMessage = targetVersionRequest.ToProtocolMessage(_peerId, CorrelationId.GenerateCorrelationId()); diff --git a/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/RpcServerSettingsTests.cs b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/RpcServerSettingsTests.cs index 56e31f730c..4ae32755b4 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/RpcServerSettingsTests.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server.Tests/UnitTests/RpcServerSettingsTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,35 +21,30 @@ #endregion -using System.Net; using FluentAssertions; using Microsoft.Extensions.Configuration; using NSubstitute; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Modules.Rpc.Server.Tests.UnitTests { public class RpcServerSettingsTests { - [Fact] + [Test] public void Constructor_Should_Set_Settings_From_Config() { const string pfxFileName = "pfx"; - const int port = 9000; - var bindAddress = IPAddress.Loopback.ToString(); var config = Substitute.For(); var rpcSection = config.GetSection("CatalystNodeConfiguration").GetSection("Rpc"); - rpcSection.GetSection("Port").Value.Returns(port.ToString()); rpcSection.GetSection("PfxFileName").Value.Returns(pfxFileName); - rpcSection.GetSection("BindAddress").Value.Returns(bindAddress); + rpcSection.GetSection("Address").Value.Returns("/ip4/127.0.0.1/tcp/9000"); - var rpcSeverSettings = new RpcServerSettings(config); + RpcServerSettings rpcSeverSettings = new(config); rpcSeverSettings.NodeConfig.Should().Be(config); rpcSeverSettings.PfxFileName.Should().Be(pfxFileName); - rpcSeverSettings.Port.Should().Be(port); - rpcSeverSettings.BindAddress.Should().Be(IPAddress.Parse(bindAddress)); + rpcSeverSettings.Address.ToString().Should().Be("/ip4/127.0.0.1/tcp/9000"); } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Server/Catalyst.Core.Modules.Rpc.Server.csproj b/src/Catalyst.Core.Modules.Rpc.Server/Catalyst.Core.Modules.Rpc.Server.csproj index 890545e292..cf615888b7 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/Catalyst.Core.Modules.Rpc.Server.csproj +++ b/src/Catalyst.Core.Modules.Rpc.Server/Catalyst.Core.Modules.Rpc.Server.csproj @@ -1,16 +1,24 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Rpc.Server - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Rpc.Server.snk true + + 1701;1702;CS8002 + + + + + + diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/AddFileToDfsRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/AddFileToDfsRequestObserver.cs index 382f3c46cf..1f463f8742 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/AddFileToDfsRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/AddFileToDfsRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,55 +27,60 @@ using System.Threading.Tasks; using Catalyst.Abstractions.Dfs; using Catalyst.Abstractions.Enumerator; -using Catalyst.Abstractions.FileTransfer; +using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.Options; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.FileTransfer; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; using Google.Protobuf; -using LibP2P; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { /// - /// The request handler to add a file to the DFS + /// The request handler to add a file to the DFS /// /// - public sealed class AddFileToDfsRequestObserver - : RequestObserverBase, + public sealed class AddFileToDfsRequestObserver + : RpcRequestObserverBase, IRpcRequestObserver { /// The download file transfer factory private readonly IDownloadFileTransferFactory _fileTransferFactory; /// The DFS - private readonly IDfs _dfs; + private readonly IDfsService _dfsService; - /// Initializes a new instance of the class. - /// The DFS. + private readonly IHashProvider _hashProvider; + + /// Initializes a new instance of the class. + /// The DFS. /// /// The download file transfer factory. + /// /// The logger. - public AddFileToDfsRequestObserver(IDfs dfs, + public AddFileToDfsRequestObserver(IDfsService dfsService, IPeerSettings peerSettings, IDownloadFileTransferFactory fileTransferFactory, + IHashProvider hashProvider, ILogger logger) : base(logger, peerSettings) { _fileTransferFactory = fileTransferFactory; - _dfs = dfs; + _dfsService = dfsService; + _hashProvider = hashProvider; } - + /// - /// /// /// /// @@ -84,15 +89,15 @@ public AddFileToDfsRequestObserver(IDfs dfs, /// protected override AddFileToDfsResponse HandleRequest(AddFileToDfsRequest addFileToDfsRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress senderAddress, ICorrelationId correlationId) { Guard.Argument(addFileToDfsRequest, nameof(addFileToDfsRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); - - var fileTransferInformation = new DownloadFileTransferInformation(PeerSettings.PeerId, - senderPeerId, channelHandlerContext.Channel, + Guard.Argument(senderAddress, nameof(senderAddress)).NotNull(); + + var fileTransferInformation = new DownloadFileTransferInformation(PeerSettings.Address, + senderAddress, channelHandlerContext.Channel, correlationId, addFileToDfsRequest.FileName, addFileToDfsRequest.FileSize); FileTransferResponseCodeTypes responseCodeType; @@ -113,18 +118,20 @@ protected override AddFileToDfsResponse HandleRequest(AddFileToDfsRequest addFil { return message; } - + var ctx = new CancellationTokenSource(); - - _fileTransferFactory.FileTransferAsync(fileTransferInformation.CorrelationId, CancellationToken.None).ContinueWith(task => - { - if (fileTransferInformation.ChunkIndicatorsTrue()) - { - OnSuccessAsync(fileTransferInformation).ConfigureAwait(false).GetAwaiter().GetResult(); - } - fileTransferInformation.Dispose(); - }, ctx.Token, TaskContinuationOptions.None, TaskScheduler.FromCurrentSynchronizationContext()).ConfigureAwait(false); + _fileTransferFactory.FileTransferAsync(fileTransferInformation.CorrelationId, CancellationToken.None) + .ContinueWith(task => + { + if (fileTransferInformation.ChunkIndicatorsTrue()) + { + OnSuccessAsync(fileTransferInformation).ConfigureAwait(false).GetAwaiter().GetResult(); + } + + fileTransferInformation.Dispose(); + }, ctx.Token) + .ConfigureAwait(false); return message; } @@ -135,20 +142,25 @@ private async Task AddFileToDfsAsync(IFileTransfe try { - Cid cid; + IFileSystemNode fileSystemNode; - using (var fileStream = File.Open(fileTransferInformation.TempPath, FileMode.Open, FileAccess.Read, FileShare.ReadWrite)) + await using (var fileStream = File.Open(fileTransferInformation.TempPath, FileMode.Open, FileAccess.Read, + FileShare.ReadWrite)) { - cid = await _dfs.AddAsync(fileStream, fileTransferInformation.FileOutputPath).ConfigureAwait(false); + fileSystemNode = await _dfsService.UnixFsApi.AddAsync(fileStream, + fileTransferInformation.FileOutputPath, + new AddFileOptions { Hash = _hashProvider.HashingAlgorithm.Name }).ConfigureAwait(false); } - fileTransferInformation.DfsHash = cid.Encode(); + fileTransferInformation.DfsHash = fileSystemNode.Id.Encode(); - Logger.Information($"Added File Name {fileTransferInformation.FileOutputPath} to DFS, Hash: {fileTransferInformation.DfsHash}"); + Logger.Information( + $"Added File Name {fileTransferInformation.FileOutputPath} to DFS, Hash: {fileTransferInformation.DfsHash}"); } catch (Exception e) { - Logger.Error(e, "Failed to handle file download OnSuccess {0}", fileTransferInformation.CorrelationId.Id); + Logger.Error(e, "Failed to handle file download OnSuccess {0}", + fileTransferInformation.CorrelationId.Id); responseCode = FileTransferResponseCodeTypes.Failed; } finally @@ -167,12 +179,12 @@ private async Task OnSuccessAsync(IFileTransferInformation fileTransferInformati var message = GetResponse(fileTransferInformation, await addFileResponseCode); var protocolMessage = - message.ToProtocolMessage(PeerSettings.PeerId, fileTransferInformation.CorrelationId); + message.ToProtocolMessage(PeerSettings.Address, fileTransferInformation.CorrelationId); // Send Response var responseMessage = new MessageDto( protocolMessage, - fileTransferInformation.RecipientId + fileTransferInformation.Recipient ); await fileTransferInformation.RecipientChannel.WriteAndFlushAsync(responseMessage).ConfigureAwait(false); @@ -180,15 +192,19 @@ private async Task OnSuccessAsync(IFileTransferInformation fileTransferInformati /// The file transfer information. /// The response code. - private AddFileToDfsResponse GetResponse(IFileTransferInformation fileTransferInformation, Enumeration responseCode) + private AddFileToDfsResponse GetResponse(IFileTransferInformation fileTransferInformation, + Enumeration responseCode) { Logger.Information("File transfer response code: " + responseCode); if (responseCode == FileTransferResponseCodeTypes.Successful) { - Logger.Information($"Initialised file transfer, FileName: {fileTransferInformation.FileOutputPath}, Chunks: {fileTransferInformation.MaxChunk.ToString()}"); + Logger.Information( + $"Initialised file transfer, FileName: {fileTransferInformation.FileOutputPath}, Chunks: {fileTransferInformation.MaxChunk.ToString()}"); } - var dfsHash = responseCode == FileTransferResponseCodeTypes.Finished ? fileTransferInformation.DfsHash : string.Empty; + var dfsHash = responseCode == FileTransferResponseCodeTypes.Finished + ? fileTransferInformation.DfsHash + : string.Empty; // Build Response var response = new AddFileToDfsResponse diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/BroadcastRawTransactionRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/BroadcastRawTransactionRequestObserver.cs index 81e7ff80d5..78c3dfc545 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/BroadcastRawTransactionRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/BroadcastRawTransactionRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,19 +23,19 @@ using Catalyst.Abstractions.IO.Events; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { public class BroadcastRawTransactionRequestObserver - : RequestObserverBase, IRpcRequestObserver + : RpcRequestObserverBase, IRpcRequestObserver { private readonly ITransactionReceivedEvent _transactionReceivedEvent; @@ -49,10 +49,10 @@ public BroadcastRawTransactionRequestObserver(ILogger logger, protected override BroadcastRawTransactionResponse HandleRequest(BroadcastRawTransactionRequest messageDto, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { - var responseCode = _transactionReceivedEvent.OnTransactionReceived(messageDto.Transaction.ToProtocolMessage(senderPeerId, correlationId)); + var responseCode = _transactionReceivedEvent.OnTransactionReceived(messageDto.Transaction.ToProtocolMessage(sender, correlationId), true); return new BroadcastRawTransactionResponse {ResponseCode = responseCode}; } diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/ChangeDataFolderRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/ChangeDataFolderRequestObserver.cs index 6488dc74e2..4bc04b1b33 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/ChangeDataFolderRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/ChangeDataFolderRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,24 +23,24 @@ using Catalyst.Abstractions.FileSystem; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { public sealed class ChangeDataFolderRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { private readonly IFileSystem _fileSystem; - public ChangeDataFolderRequestObserver(IPeerSettings peerSettings, + public ChangeDataFolderRequestObserver(IPeerSettings peerSettings, IFileSystem fileSystem, ILogger logger) : base(logger, peerSettings) { @@ -49,12 +49,12 @@ public ChangeDataFolderRequestObserver(IPeerSettings peerSettings, protected override SetPeerDataFolderResponse HandleRequest(SetPeerDataFolderRequest setDataFolderRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(setDataFolderRequest, nameof(setDataFolderRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Debug("received message of type SetPeerDataFolderRequest"); diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetDeltaRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetDeltaRequestObserver.cs index b955992b9e..0551130156 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetDeltaRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetDeltaRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,20 +23,21 @@ using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Core.Lib.Util; -using Catalyst.Protocol.Peer; +using Catalyst.Core.Modules.Dfs.Extensions; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { public sealed class GetDeltaRequestObserver - : RequestObserverBase, IRpcRequestObserver + : RpcRequestObserverBase, IRpcRequestObserver { private readonly IDeltaCache _deltaCache; @@ -49,16 +50,16 @@ public GetDeltaRequestObserver(IDeltaCache deltaCache, protected override GetDeltaResponse HandleRequest(GetDeltaRequest getDeltaRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(getDeltaRequest, nameof(getDeltaRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Verbose("received message of type GetDeltaRequest:"); Logger.Verbose("{getDeltaRequest}", getDeltaRequest); - var cid = CidHelper.Cast(getDeltaRequest.DeltaDfsHash.ToByteArray()); + var cid = getDeltaRequest.DeltaDfsHash.ToByteArray().ToCid(); _deltaCache.TryGetOrAddConfirmedDelta(cid, out var delta); diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetFileFromDfsRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetFileFromDfsRequestObserver.cs index 755e68f7c1..bf1da57843 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetFileFromDfsRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetFileFromDfsRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,22 +26,22 @@ using System.Threading.Tasks; using Catalyst.Abstractions.Dfs; using Catalyst.Abstractions.Enumerator; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.FileTransfer; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; using Catalyst.Protocol.Rpc.Node; using Catalyst.Protocol.Wire; using Dawn; using DotNetty.Transport.Channels; using Google.Protobuf; -using LibP2P; +using Lib.P2P; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers @@ -51,27 +51,27 @@ namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers /// /// public sealed class GetFileFromDfsRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { /// The upload file transfer factory private readonly IUploadFileTransferFactory _fileTransferFactory; /// The DFS - private readonly IDfs _dfs; + private readonly IDfsService _dfsService; /// Initializes a new instance of the class. - /// The DFS. + /// The DFS. /// /// The upload file transfer factory. /// The logger. - public GetFileFromDfsRequestObserver(IDfs dfs, + public GetFileFromDfsRequestObserver(IDfsService dfsService, IPeerSettings peerSettings, IUploadFileTransferFactory fileTransferFactory, ILogger logger) : base(logger, peerSettings) { _fileTransferFactory = fileTransferFactory; - _dfs = dfs; + _dfsService = dfsService; } /// @@ -83,12 +83,12 @@ public GetFileFromDfsRequestObserver(IDfs dfs, /// protected override GetFileFromDfsResponse HandleRequest(GetFileFromDfsRequest getFileFromDfsRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress senderAddress, ICorrelationId correlationId) { Guard.Argument(getFileFromDfsRequest, nameof(getFileFromDfsRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(senderAddress, nameof(senderAddress)).NotNull(); long fileLen = 0; @@ -100,13 +100,13 @@ protected override GetFileFromDfsResponse HandleRequest(GetFileFromDfsRequest ge { responseCodeType = await Task.Run(async () => { - var stream = await _dfs.ReadAsync(Cid.Decode(getFileFromDfsRequest.DfsHash)) + var stream = await _dfsService.UnixFsApi.ReadFileAsync(Cid.Decode(getFileFromDfsRequest.DfsHash)) .ConfigureAwait(false); fileLen = stream.Length; using (var fileTransferInformation = new UploadFileTransferInformation( stream, - senderPeerId, - PeerSettings.PeerId, + senderAddress, + PeerSettings.Address, channelHandlerContext.Channel, correlationId )) diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetInfoRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetInfoRequestObserver.cs index 3ff4e67bec..9d550e7fbc 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetInfoRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetInfoRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,21 +22,21 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; using Microsoft.Extensions.Configuration; +using MultiFormats; using Newtonsoft.Json; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { public sealed class GetInfoRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { private readonly IConfigurationRoot _config; @@ -50,12 +50,12 @@ public GetInfoRequestObserver(IPeerSettings peerSettings, protected override GetInfoResponse HandleRequest(GetInfoRequest getInfoRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(getInfoRequest, nameof(getInfoRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Debug("received message of type GetInfoRequest"); Logger.Debug("message content is {0}", getInfoRequest); diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetMempoolRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetMempoolRequestObserver.cs index 6c562f1642..2b3a358134 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetMempoolRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetMempoolRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,25 +28,28 @@ using Catalyst.Abstractions.Mempool; using Catalyst.Abstractions.P2P; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; using Catalyst.Protocol.Rpc.Node; -using Catalyst.Protocol.Wire; +using Catalyst.Protocol.Transaction; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { public sealed class GetMempoolRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { - private readonly IMempool _mempool; + private readonly IMempool _mempool; private readonly IMapperProvider _mappingProvider; public GetMempoolRequestObserver(IPeerSettings peerSettings, - IMempool mempool, + IMempool mempool, IMapperProvider mappingProvider, ILogger logger) : base(logger, peerSettings) @@ -59,25 +62,25 @@ public GetMempoolRequestObserver(IPeerSettings peerSettings, /// /// /// - /// + /// /// /// protected override GetMempoolResponse HandleRequest(GetMempoolRequest getMempoolRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(getMempoolRequest, nameof(getMempoolRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Debug("GetMempoolRequestHandler starting ..."); try { Logger.Debug("Received GetMempoolRequest message with content {0}", getMempoolRequest); - var mempoolTransactions = _mempool.Repository.GetAll() - .Select(x => x.ToProtoBuff(_mappingProvider)); + var mempoolTransactions = _mempool.Service.GetAll() + .Select(x => x.ToProtoBuff(_mappingProvider)); return new GetMempoolResponse { diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetPeerInfoRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetPeerInfoRequestObserver.cs index 00100adfa4..deb4dd42d5 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetPeerInfoRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetPeerInfoRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,24 +23,25 @@ using System.Linq; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using Dawn; -using DotNetty.Transport.Channels; using Google.Protobuf.WellKnownTypes; using Serilog; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; +using DotNetty.Transport.Channels; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { /// - /// The GetPeerInfoRequestObserver + /// The GetPeerInfoRequestObserver /// public sealed class GetPeerInfoRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { private readonly IPeerRepository _peerRepository; @@ -54,31 +55,27 @@ public GetPeerInfoRequestObserver(IPeerSettings peerSettings, } /// - /// Handle the request for GetPeerInfo + /// Handle the request for GetPeerInfo /// /// The request /// The channel handler context - /// The sender peer identifier + /// The sender peer identifier /// The correlationId /// The GetPeerInfoResponse protected override GetPeerInfoResponse HandleRequest(GetPeerInfoRequest getPeerInfoRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(getPeerInfoRequest, nameof(getPeerInfoRequest)).NotNull(); - Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Debug("received message of type GetPeerInfoRequest"); - var ip = getPeerInfoRequest.Ip; - - var peerInfo = _peerRepository.FindAll(m => m.PeerId.Ip == ip - && m.PeerId.PublicKey == getPeerInfoRequest.PublicKey) + var peerInfo = _peerRepository.GetPeersByAddress(getPeerInfoRequest.Address) .Select(x => new PeerInfo { - PeerId = x.PeerId, + Address = x.Address.ToString(), Reputation = x.Reputation, IsBlacklisted = x.BlackListed, IsUnreachable = x.IsAwolPeer, @@ -94,5 +91,3 @@ protected override GetPeerInfoResponse HandleRequest(GetPeerInfoRequest getPeerI } } } - - diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetVersionRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetVersionRequestObserver.cs index 7ce1c92767..aaa49c7532 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetVersionRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/GetVersionRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,20 +22,20 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.IO.Observers; using Catalyst.Core.Lib.Util; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { public sealed class GetVersionRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { public GetVersionRequestObserver(IPeerSettings peerSettings, @@ -47,17 +47,17 @@ public GetVersionRequestObserver(IPeerSettings peerSettings, /// /// /// - /// + /// /// /// protected override VersionResponse HandleRequest(VersionRequest versionRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(versionRequest, nameof(versionRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Debug("received message of type VersionRequest"); diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerBlackListingRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerBlackListingRequestObserver.cs index 7caf457052..6398e1afe5 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerBlackListingRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerBlackListingRequestObserver.cs @@ -1,8 +1,7 @@ - #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,29 +23,27 @@ using System.Linq; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Core.Lib.P2P.Repository; -using Catalyst.Core.Lib.Util; -using Catalyst.Protocol.Peer; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Protocol.Rpc.Node; using Dawn; -using DotNetty.Transport.Channels; -using Google.Protobuf; using Serilog; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; +using DotNetty.Transport.Channels; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { public sealed class PeerBlackListingRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { /// - /// The PeerBlackListingRequestHandler + /// The PeerBlackListingRequestHandler /// private readonly IPeerRepository _peerRepository; - + public PeerBlackListingRequestObserver(IPeerSettings peerSettings, ILogger logger, IPeerRepository peerRepository) @@ -54,47 +51,42 @@ public PeerBlackListingRequestObserver(IPeerSettings peerSettings, { _peerRepository = peerRepository; } - + /// - /// /// /// /// - /// + /// /// /// - protected override SetPeerBlacklistResponse HandleRequest(SetPeerBlacklistRequest setPeerBlackListRequest, + protected override SetPeerBlackListResponse HandleRequest(SetPeerBlackListRequest setPeerBlackListRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(setPeerBlackListRequest, nameof(setPeerBlackListRequest)).NotNull(); - Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Information("received message of type PeerBlackListingRequest"); - - var peerItem = _peerRepository.GetAll() - .FirstOrDefault(m => m.PeerId.Ip == setPeerBlackListRequest.Ip - && m.PeerId.PublicKey.KeyToString() == setPeerBlackListRequest.PublicKey.KeyToString()); + + var peerItem = _peerRepository.GetAll().FirstOrDefault(m => m.Address == setPeerBlackListRequest.Address); return peerItem == null - ? ReturnResponse(false, ByteString.Empty, ByteString.Empty) - : ReturnResponse(setPeerBlackListRequest.Blacklist, setPeerBlackListRequest.PublicKey, setPeerBlackListRequest.Ip); + ? ReturnResponse(false, string.Empty) + : ReturnResponse(setPeerBlackListRequest.Blacklist, peerItem.Address.ToString()); } /// - /// Returns the response. + /// Returns the response. /// /// if set to true [blacklist]. /// The public key. /// The ip. - private SetPeerBlacklistResponse ReturnResponse(bool blacklist, ByteString publicKey, ByteString ip) + private SetPeerBlackListResponse ReturnResponse(bool blacklist, string address) { - return new SetPeerBlacklistResponse + return new SetPeerBlackListResponse { Blacklist = blacklist, - Ip = ip, - PublicKey = publicKey + Address = address }; } } diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerCountRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerCountRequestObserver.cs index 296e2f212b..91bc32be57 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerCountRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerCountRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,27 +26,29 @@ using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Core.Lib.P2P.Repository; -using Catalyst.Protocol.Peer; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Protocol.Rpc.Node; using Dawn; -using DotNetty.Transport.Channels; using Serilog; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using DotNetty.Transport.Channels; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { /// - /// Peer count request handler + /// Peer count request handler /// /// public sealed class PeerCountRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { /// The peer discovery private readonly IPeerRepository _peerRepository; - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The peer discovery. /// The logger. @@ -57,23 +59,22 @@ public PeerCountRequestObserver(IPeerSettings peerSettings, { _peerRepository = peerRepository; } - + /// - /// /// /// /// - /// + /// /// /// protected override GetPeerCountResponse HandleRequest(GetPeerCountRequest getPeerCountRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(getPeerCountRequest, nameof(getPeerCountRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); var peerCount = _peerRepository.GetAll().Count(); return new GetPeerCountResponse diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerListRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerListRequestObserver.cs index 23984ea579..d0cd468403 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerListRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerListRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,12 +26,14 @@ using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Core.Lib.P2P.Repository; -using Catalyst.Protocol.Peer; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Protocol.Rpc.Node; using Dawn; -using DotNetty.Transport.Channels; using Serilog; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using DotNetty.Transport.Channels; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { @@ -40,7 +42,7 @@ namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers /// /// public sealed class PeerListRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { /// @@ -49,7 +51,7 @@ public sealed class PeerListRequestObserver private readonly IPeerRepository _peerRepository; /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// /// The logger. @@ -61,26 +63,25 @@ public PeerListRequestObserver(IPeerSettings peerSettings, { _peerRepository = peerRepository; } - + /// - /// /// /// /// - /// + /// /// /// protected override GetPeerListResponse HandleRequest(GetPeerListRequest getPeerListRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(getPeerListRequest, nameof(getPeerListRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Debug("received message of type PeerListRequest"); - var peers = _peerRepository.GetAll().Select(x => x.PeerId); + var peers = _peerRepository.GetAll().Select(x => x.Address.ToString()); var response = new GetPeerListResponse(); response.Peers.AddRange(peers); diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerReputationRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerReputationRequestObserver.cs index 3554bb6e75..26238e352c 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerReputationRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/PeerReputationRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,25 +23,24 @@ using System.Linq; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Core.Lib.P2P.Repository; -using Catalyst.Core.Lib.Util; -using Catalyst.Protocol.Peer; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Protocol.Rpc.Node; using Dawn; -using DotNetty.Transport.Channels; using Serilog; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; +using DotNetty.Transport.Channels; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { public sealed class PeerReputationRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { /// - /// The PeerReputationRequestHandler + /// The PeerReputationRequestHandler /// private readonly IPeerRepository _peerRepository; @@ -54,31 +53,27 @@ public PeerReputationRequestObserver(IPeerSettings peerSettings, } /// - /// /// /// /// - /// + /// /// /// protected override GetPeerReputationResponse HandleRequest(GetPeerReputationRequest getPeerReputationRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(getPeerReputationRequest, nameof(getPeerReputationRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Debug("received message of type PeerReputationRequest"); return new GetPeerReputationResponse { - Reputation = _peerRepository.GetAll().Where(m => m.PeerId.Ip == getPeerReputationRequest.Ip - && m.PeerId.PublicKey.KeyToString() == getPeerReputationRequest.PublicKey.KeyToString()) + Reputation = _peerRepository.GetAll().Where(m => m.Address == getPeerReputationRequest.Address) .Select(x => x.Reputation).DefaultIfEmpty(int.MinValue).First() }; } } } - - diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/RemovePeerRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/RemovePeerRequestObserver.cs index 647adf9caf..f953eee064 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/RemovePeerRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/RemovePeerRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,34 +21,31 @@ #endregion -using System.Linq; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; -using Catalyst.Protocol.Peer; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Protocol.Rpc.Node; using Dawn; -using DotNetty.Transport.Channels; using Serilog; -using SharpRepository.Repository.Specifications; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; +using DotNetty.Transport.Channels; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { /// - /// Remove Peer handler + /// Remove Peer handler /// /// public sealed class RemovePeerRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { /// The peer discovery private readonly IPeerRepository _peerRepository; - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// /// The peer discovery. /// The logger. @@ -58,38 +55,25 @@ public RemovePeerRequestObserver(IPeerSettings peerSettings, { _peerRepository = peerRepository; } - + /// - /// /// /// /// - /// + /// /// /// protected override RemovePeerResponse HandleRequest(RemovePeerRequest removePeerRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(removePeerRequest, nameof(removePeerRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Debug("Received message of type RemovePeerRequest"); - uint peerDeletedCount = 0; - - var publicKeyIsEmpty = removePeerRequest.PublicKey.IsEmpty; - - var peersToDelete = _peerRepository.FindAll(new Specification(peer => - peer.PeerId.Ip.SequenceEqual(removePeerRequest.PeerIp) && - (publicKeyIsEmpty || peer.PeerId.PublicKey.SequenceEqual(removePeerRequest.PublicKey.ToByteArray())))).ToArray(); - - foreach (var peerToDelete in peersToDelete) - { - _peerRepository.Delete(peerToDelete); - peerDeletedCount += 1; - } + var peerDeletedCount = _peerRepository.DeletePeersByAddress(removePeerRequest.Address); return new RemovePeerResponse { diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/SignMessageRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/SignMessageRequestObserver.cs index 65cac946c8..e2b92c611d 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/SignMessageRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/SignMessageRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,21 +22,21 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { public sealed class SignMessageRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { private readonly IKeySigner _keySigner; @@ -54,17 +54,17 @@ public SignMessageRequestObserver(IPeerSettings peerSettings, /// /// /// - /// + /// /// /// protected override SignMessageResponse HandleRequest(SignMessageRequest signMessageRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(signMessageRequest, nameof(signMessageRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Debug("received message of type SignMessageRequest"); var decodedMessage = signMessageRequest.Message.ToByteArray(); diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/TransferFileBytesRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/TransferFileBytesRequestObserver.cs index 6d7c8a09d0..405a79774b 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/TransferFileBytesRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/TransferFileBytesRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,23 +21,23 @@ #endregion -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Types; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; using Google.Protobuf; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { public sealed class TransferFileBytesRequestObserver - : RequestObserverBase, + : RpcRequestObserverBase, IRpcRequestObserver { /// The download file transfer factory @@ -60,17 +60,17 @@ public TransferFileBytesRequestObserver(IDownloadFileTransferFactory fileTransfe /// /// /// - /// + /// /// /// protected override TransferFileBytesResponse HandleRequest(TransferFileBytesRequest transferFileBytesRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(transferFileBytesRequest, nameof(transferFileBytesRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Debug("received message of type TransferFileBytesRequest"); FileTransferResponseCodeTypes responseCodeType = _fileTransferFactory.DownloadChunk(transferFileBytesRequest); diff --git a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/VerifyMessageRequestObserver.cs b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/VerifyMessageRequestObserver.cs index 28997c8828..ba1e62e999 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/VerifyMessageRequestObserver.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/IO/Observers/VerifyMessageRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,20 +24,20 @@ using System; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Observers; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers; using Catalyst.Protocol.Rpc.Node; using Dawn; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server.IO.Observers { public sealed class VerifyMessageRequestObserver - : RequestObserverBase, IRpcRequestObserver + : RpcRequestObserverBase, IRpcRequestObserver { private readonly IKeySigner _keySigner; @@ -54,20 +54,20 @@ public VerifyMessageRequestObserver(IPeerSettings peerSettings, /// /// - /// + /// /// /// protected override VerifyMessageResponse HandleRequest(VerifyMessageRequest verifyMessageRequest, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { Guard.Argument(verifyMessageRequest, nameof(verifyMessageRequest)).NotNull(); Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); - Guard.Argument(senderPeerId, nameof(senderPeerId)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); Logger.Debug("received message of type VerifyMessageRequest"); - var decodedMessage = verifyMessageRequest.Message.ToByteArray(); + var decodedMessage = verifyMessageRequest.Message; var decodedPublicKey = verifyMessageRequest.PublicKey.ToByteArray(); var decodedSignature = verifyMessageRequest.Signature.ToByteArray(); var signatureContext = verifyMessageRequest.SigningContext; @@ -95,7 +95,7 @@ protected override VerifyMessageResponse HandleRequest(VerifyMessageRequest veri return ReturnResponse(false); } - var result = _keySigner.Verify(signature, decodedMessage, signatureContext); + var result = _keySigner.Verify(signature, decodedMessage.Span, signatureContext); Logger.Debug("message content is {0}", verifyMessageRequest.Message); diff --git a/src/Catalyst.Core.Modules.Rpc.Server/RpcServer.cs b/src/Catalyst.Core.Modules.Rpc.Server/RpcServer.cs index 28a67a379e..9ac7561c41 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/RpcServer.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/RpcServer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,18 +28,19 @@ using System.Threading; using System.Threading.Tasks; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.Rpc; -using Catalyst.Core.Lib.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Rpc; using Catalyst.Protocol.Wire; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server { - public sealed class RpcServer : TcpServer, IRpcServer + public sealed class RpcServer : TcpServer>, IRpcServer { private readonly IEnumerable _requestHandlers; private readonly CancellationTokenSource _cancellationSource; @@ -50,7 +51,7 @@ public sealed class RpcServer : TcpServer, IRpcServer public RpcServer(IRpcServerSettings settings, ILogger logger, - ITcpServerChannelFactory channelFactory, + ITcpServerChannelFactory> channelFactory, ICertificateStore certificateStore, IEnumerable requestHandlers, ITcpServerEventLoopGroupFactory eventEventLoopGroupFactory) @@ -64,7 +65,7 @@ public RpcServer(IRpcServerSettings settings, public override async Task StartAsync() { - var observableSocket = await ChannelFactory.BuildChannelAsync(EventLoopGroupFactory, Settings.BindAddress, Settings.Port, _certificate).ConfigureAwait(false); + var observableSocket = await ChannelFactory.BuildChannelAsync(EventLoopGroupFactory, Settings.Address, _certificate).ConfigureAwait(false); Channel = observableSocket.Channel; MessageStream = observableSocket.MessageStream; _requestHandlers.ToList().ForEach(h => h.StartObserving(MessageStream)); diff --git a/src/Catalyst.Core.Modules.Rpc.Server/RpcServerModule.cs b/src/Catalyst.Core.Modules.Rpc.Server/RpcServerModule.cs index 3fe37abdf9..ef94a5f597 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/RpcServerModule.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/RpcServerModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,9 +23,12 @@ using System; using Autofac; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.Rpc; using Catalyst.Core.Modules.Rpc.Server.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Rpc; +using Catalyst.Protocol.Wire; using Serilog; namespace Catalyst.Core.Modules.Rpc.Server @@ -34,31 +37,31 @@ public sealed class RpcServerModule : Module { protected override void Load(ContainerBuilder builder) { - builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As>>().SingleInstance(); builder.RegisterType().As().SingleInstance(); builder.RegisterType().As(); - async void BuildCallback(IContainer container) + // builder.RegisterBuildCallback(BuildCallback); + builder.RegisterBuildCallback(c => { - if (container == null) - { - throw new ArgumentNullException(nameof(container)); - } - - var logger = container.Resolve(); - try - { - var rpcServer = container.Resolve(); - await rpcServer.StartAsync().ConfigureAwait(false); - } - catch (Exception e) - { - logger.Error(e, "Error loading API"); - } - } + // TODO TheNewAutonomy + /* if (container == null) + { + throw new ArgumentNullException(nameof(container)); + } + + var logger = container.Resolve(); + try + { + var rpcServer = container.Resolve(); + await rpcServer.StartAsync().ConfigureAwait(false); + } + catch (Exception e) + { + logger.Error(e, "Error loading API"); + }*/ + }); - builder.RegisterBuildCallback(BuildCallback); - base.Load(builder); } } diff --git a/src/Catalyst.Core.Modules.Rpc.Server/RpcServerSettings.cs b/src/Catalyst.Core.Modules.Rpc.Server/RpcServerSettings.cs index 08922a2ae7..d8088182b0 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/RpcServerSettings.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/RpcServerSettings.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,6 +25,7 @@ using Catalyst.Abstractions.Rpc; using Dawn; using Microsoft.Extensions.Configuration; +using MultiFormats; namespace Catalyst.Core.Modules.Rpc.Server { @@ -46,14 +47,12 @@ public RpcServerSettings(IConfigurationRoot rootSection) var section = rootSection.GetSection("CatalystNodeConfiguration").GetSection("Rpc"); - Port = int.Parse(section.GetSection("Port").Value); PfxFileName = section.GetSection("PfxFileName").Value; - BindAddress = IPAddress.Parse(section.GetSection("BindAddress").Value); + Address = section.GetSection("Address").Value; } public IConfigurationRoot NodeConfig { get; } - public int Port { get; } - public IPAddress BindAddress { get; } + public MultiAddress Address { get; } public string PfxFileName { get; } } } diff --git a/src/Catalyst.Core.Modules.Rpc.Server/Transport/Channels/RpcServerChannelFactory.cs b/src/Catalyst.Core.Modules.Rpc.Server/Transport/Channels/RpcServerChannelFactory.cs index 90fb58f319..5069afdd88 100644 --- a/src/Catalyst.Core.Modules.Rpc.Server/Transport/Channels/RpcServerChannelFactory.cs +++ b/src/Catalyst.Core.Modules.Rpc.Server/Transport/Channels/RpcServerChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,36 +23,36 @@ using System; using System.Collections.Generic; -using System.Net; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; using Catalyst.Abstractions.IO.Handlers; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Rpc.Authentication; using Catalyst.Abstractions.Rpc.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Codecs; -using Catalyst.Core.Lib.IO.Handlers; -using Catalyst.Core.Lib.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Codecs; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Wire; using DotNetty.Codecs.Protobuf; using DotNetty.Transport.Channels; +using MultiFormats; namespace Catalyst.Core.Modules.Rpc.Server.Transport.Channels { - public class RpcServerChannelFactory : TcpServerChannelFactory + public class RpcServerChannelFactory : TcpServerChannelFactory> { private readonly IRpcMessageCorrelationManager _correlationManger; private readonly IAuthenticationStrategy _authenticationStrategy; private readonly IKeySigner _keySigner; private readonly IPeerIdValidator _peerIdValidator; - private readonly IObservableServiceHandler _observableServiceHandler; + private readonly IObservableServiceHandler> _observableServiceHandler; private readonly SigningContext _signingContext; protected override Func> HandlerGenerationFunction @@ -102,24 +102,22 @@ public RpcServerChannelFactory(IRpcMessageCorrelationManager correlationManger, _keySigner = keySigner; _peerIdValidator = peerIdValidator; _signingContext = new SigningContext {NetworkType = peerSettings.NetworkType, SignatureType = SignatureType.ProtocolRpc}; - _observableServiceHandler = new ObservableServiceHandler(observableScheduler); + _observableServiceHandler = new RpcObservableServiceHandler(observableScheduler); } /// /// /// Local TLS certificate /// - public override async Task BuildChannelAsync(IEventLoopGroupFactory handlerEventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + public override async Task>> BuildChannelAsync(IEventLoopGroupFactory handlerEventLoopGroupFactory, + MultiAddress address, X509Certificate2 certificate = null) { - var channel = await BootstrapAsync(handlerEventLoopGroupFactory, targetAddress, targetPort, certificate).ConfigureAwait(false); + var channel = await BootstrapAsync(handlerEventLoopGroupFactory, address, certificate).ConfigureAwait(false); var messageStream = _observableServiceHandler.MessageStream; - return new ObservableChannel(messageStream - ?? Observable.Never>(), channel); + return new ObservableChannel>(messageStream ?? Observable.Never>(), channel); } } } diff --git a/src/Catalyst.Core.Modules.Sync.Tests/Catalyst.Core.Modules.Sync.Tests.csproj b/src/Catalyst.Core.Modules.Sync.Tests/Catalyst.Core.Modules.Sync.Tests.csproj new file mode 100644 index 0000000000..d9b3827050 --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync.Tests/Catalyst.Core.Modules.Sync.Tests.csproj @@ -0,0 +1,34 @@ + + + + net6.0 + + false + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + diff --git a/src/Catalyst.Core.Modules.Sync.Tests/IntergrationTests/SyncIntegrationTests.cs b/src/Catalyst.Core.Modules.Sync.Tests/IntergrationTests/SyncIntegrationTests.cs new file mode 100644 index 0000000000..160ebe87e5 --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync.Tests/IntergrationTests/SyncIntegrationTests.cs @@ -0,0 +1,187 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +//using System; +//using System.Collections.Generic; +//using System.IO; +//using System.Linq; +//using System.Threading; +//using System.Threading.Tasks; +//using Autofac; +//using Catalyst.Abstractions.Consensus.Deltas; +//using Catalyst.Abstractions.Dfs; +//using Catalyst.Abstractions.FileSystem; +//using Catalyst.Abstractions.Hashing; +//using Catalyst.Abstractions.Ledger; +//using Catalyst.Abstractions.Options; +//using Catalyst.Abstractions.Sync.Interfaces; +//using Catalyst.Core.Lib.Extensions; +//using Catalyst.Core.Modules.Cryptography.BulletProofs; +//using Catalyst.Protocol.Deltas; +//using Catalyst.TestUtils; +//using Google.Protobuf; +//using Google.Protobuf.WellKnownTypes; +//using NSubstitute; +//using NUnit.Framework; +//using Catalyst.Core.Modules.Dfs.Extensions; + +//namespace Catalyst.Core.Modules.Sync.Tests.IntegrationTests +//{ +// public class SyncIntegrationTests : FileSystemBasedTest +// { +// private CancellationTokenSource _endOfTestCancellationSource; +// private ILifetimeScope _scope; +// private List _nodes; + +// [SetUp] +// public void Init() +// { +// Setup(TestContext.CurrentContext); +// _endOfTestCancellationSource = new CancellationTokenSource(); + +// var context = new FfiWrapper(); + +// var poaNodeDetails = Enumerable.Range(0, 3).Select(i => +// { +// var fileSystem = Substitute.For(); + +// var path = Path.Combine(FileSystem.GetCatalystDataDir().FullName, $"producer{i}"); +// fileSystem.GetCatalystDataDir().Returns(new DirectoryInfo(path)); + +// var privateKey = context.GeneratePrivateKey(); +// var publicKey = privateKey.GetPublicKey(); +// var nodeSettings = PeerSettingsHelper.TestPeerSettings(publicKey.Bytes, 2000 + i); +// var peerIdentifier = nodeSettings.Address; +// var name = $"producer{i.ToString()}"; +// var dfs = TestDfs.GetTestDfs(fileSystem); +// return new { index = i, name, privateKey, nodeSettings, peerIdentifier, dfs, fileSystem }; +// } +// ).ToList(); + +// var peerIdentifiers = poaNodeDetails.Select(n => n.Addressentifier).ToList(); + +// _nodes = new List(); +// foreach (var nodeDetails in poaNodeDetails) +// { +// nodeDetails.dfs.Options.Discovery.BootstrapPeers = poaNodeDetails.Except(new[] { nodeDetails }) +// .Select(x => x.dfs.LocalPeer.Addresses.First()); + +// var node = new PoaTestNode(nodeDetails.index, false, nodeDetails.fileSystem); +// _nodes.Add(node); +// } +// } + +// //todo +// [Test] +// public async Task Can_Sync_From_Another_Nodes() +// { +// var manualResetEvent = new ManualResetEvent(false); +// var tasks = _nodes.Count(); +// var utcNow = DateTime.UtcNow; +// var cids = new List(); +// Delta previousDelta = null; +// for (var j = 0; j < _nodes.Count(); j++) +// { +// var nodeJ = _nodes[j]; +// var ledger = nodeJ.GetContainerProvider().Container.Resolve(); +// var deltaHashProvider = nodeJ.GetContainerProvider().Container.Resolve(); +// var hashProvider = nodeJ.GetContainerProvider().Container.Resolve(); +// var dfsService = nodeJ.GetContainerProvider().Container.Resolve(); +// var deltaCache = nodeJ.GetContainerProvider().Container.Resolve(); +// var sync = nodeJ.GetContainerProvider().Container.Resolve(); +// sync.SyncCompleted.Subscribe(x => +// { +// tasks--; +// if (tasks <= 0) +// { +// _endOfTestCancellationSource.Cancel(); +// manualResetEvent.Set(); +// } +// }); + +// if (previousDelta == null) +// { +// deltaCache.TryGetOrAddConfirmedDelta(deltaCache.GenesisHash, out previousDelta); +// } + +// for (var i = 1; i < 25; i++) +// { +// if (j == 2) +// { +// break; +// } + +// if (j == 1 && i > 15) +// { +// break; +// } + +// var delta = new Delta +// { +// StateRoot = previousDelta.StateRoot.ToByteString(), +// PreviousDeltaDfsHash = ledger.LatestKnownDelta.ToArray().ToByteString(), +// MerkleRoot = hashProvider.ComputeMultiHash(ledger.LatestKnownDelta.ToArray()).ToCid().ToArray() +// .ToByteString(), +// TimeStamp = Timestamp.FromDateTime(utcNow.AddMilliseconds(i + 1)) +// }; + +// var node = await dfsService.UnixFsApi.AddAsync(delta.ToByteArray().ToMemoryStream(), string.Empty, +// new AddFileOptions { Hash = hashProvider.HashingAlgorithm.Name }, CancellationToken.None) +// .ConfigureAwait(false); + +// cids.Add(node.Id.Hash.ToBase32()); + +// deltaHashProvider.TryUpdateLatestHash(ledger.LatestKnownDelta, node.Id); +// } +// } + +// Task.Run(() => +// { +// _nodes[0].PeerActive += (peerAddress) => +// { +// _nodes.ForEach(async node => await node.RegisterPeerAddressAsync(peerAddress)); +// }; +// _nodes[0].RunAsync(_endOfTestCancellationSource.Token); +// }); +// Task.Run(() => +// { +// _nodes[1].PeerActive += (peerAddress) => +// { +// _nodes.ForEach(async node => await node.RegisterPeerAddressAsync(peerAddress)); +// }; +// _nodes[1].RunAsync(_endOfTestCancellationSource.Token); +// }); + +// Task.Run(() => +// { +// _nodes[2].PeerActive += (peerAddress) => +// { +// _nodes.ForEach(async node => await node.RegisterPeerAddressAsync(peerAddress)); +// }; +// _nodes[2].RunAsync(_endOfTestCancellationSource.Token); +// }); + +// manualResetEvent.WaitOne(TimeSpan.FromMinutes(15)); +// } +// } +//} diff --git a/src/Catalyst.Core.Modules.Sync.Tests/UnitTests/DeltaHeightWatcherUnitTests.cs b/src/Catalyst.Core.Modules.Sync.Tests/UnitTests/DeltaHeightWatcherUnitTests.cs new file mode 100644 index 0000000000..fcf0c04259 --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync.Tests/UnitTests/DeltaHeightWatcherUnitTests.cs @@ -0,0 +1,161 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.P2P; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Core.Modules.Dfs.Extensions; +using Catalyst.Core.Modules.Hashing; +using Catalyst.Core.Modules.Sync.Watcher; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.IPPN; +using Catalyst.Protocol.Wire; +using Catalyst.TestUtils; +using FluentAssertions; +using Google.Protobuf; +using MultiFormats.Registry; +using NSubstitute; +using System.Collections.Generic; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Threading.Tasks; +using NUnit.Framework; +using MultiFormats; +using LibP2P = Lib.P2P; +using Catalyst.Abstractions.Dfs.CoreApi; +using System; + +namespace Catalyst.Core.Modules.Sync.Tests.UnitTests +{ + public class DeltaHeightWatcherUnitTests + { + private IPeerClient _peerClient; + private IHashProvider _hashProvider; + private IPeerService _peerService; + private ISwarmApi _swarmApi; + private ReplaySubject _deltaHeightReplaySubject; + + [SetUp] + public void Init() + { + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + _peerService = Substitute.For(); + _peerClient = Substitute.For(); + _swarmApi = Substitute.For(); + _deltaHeightReplaySubject = new ReplaySubject(1); + _peerService.MessageStream.Returns(_deltaHeightReplaySubject.AsObservable()); + } + + private void GeneratePeers(int count) + { + List peerList = new(); + + for (var i = 0; i < count; i++) + { + var address = MultiAddressHelper.GetAddress(i.ToString(), port: i); + var peer = new LibP2P.Peer + { + Id = address.PeerId, + ConnectedAddress = address + }; + peerList.Add(peer); + } + + _swarmApi.PeersAsync(default).Returns(peerList); + } + + [Test] + public async Task GetHighestDeltaIndexAsync_Should_Return_DeltaIndex() + { + var deltaHeight = 100u; + GeneratePeers(100); + + _peerClient.When(x => x.SendMessageToPeersAsync(Arg.Any(), Arg.Any>())).Do(x => + { + var peerIds = (IEnumerable) x[1]; + foreach (var peerId in peerIds) + { + var deltaHeightResponse = new LatestDeltaHashResponse + { + DeltaIndex = new DeltaIndex { Cid = _hashProvider.ComputeUtf8MultiHash(deltaHeight.ToString()).ToCid().ToArray().ToByteString(), Height = deltaHeight }, + IsSync = true + }; + + _deltaHeightReplaySubject.OnNext(deltaHeightResponse.ToProtocolMessage(peerId, CorrelationId.GenerateCorrelationId())); + } + }); + + DeltaHeightWatcher deltaHeightWatcher = new(_peerClient, _swarmApi, _peerService); + deltaHeightWatcher.Start(); + + var deltaIndex = await deltaHeightWatcher.WaitForDeltaIndexAsync(TimeSpan.FromSeconds(10)); + + deltaIndex.Height.Should().Be(deltaHeight); + } + + [Test] + public async Task GetHighestDeltaIndexAsync_Should_Return_Highest_Syncd_DeltaIndex() + { + var deltaHeight = 100u; + GeneratePeers(100); + + _peerClient.When(x => x.SendMessageToPeersAsync(Arg.Any(), Arg.Any>())).Do(x => + { + var peerIds = (IEnumerable) x[1]; + foreach (var peerId in peerIds) + { + if (peerId.GetPort() > 50) + { + var deltaHeightResponse = new LatestDeltaHashResponse + { + DeltaIndex = new DeltaIndex { Cid = _hashProvider.ComputeUtf8MultiHash(deltaHeight.ToString()).ToCid().ToArray().ToByteString(), Height = deltaHeight }, + IsSync = true + }; + + _deltaHeightReplaySubject.OnNext(deltaHeightResponse.ToProtocolMessage(peerId, CorrelationId.GenerateCorrelationId())); + } + else + { + var badHeight = 999u; + var deltaHeightResponse = new LatestDeltaHashResponse + { + DeltaIndex = new DeltaIndex { Cid = _hashProvider.ComputeUtf8MultiHash(badHeight.ToString()).ToCid().ToArray().ToByteString(), Height = badHeight }, + IsSync = false + }; + + _deltaHeightReplaySubject.OnNext(deltaHeightResponse.ToProtocolMessage(peerId, CorrelationId.GenerateCorrelationId())); + } + + } + }); + + DeltaHeightWatcher deltaHeightWatcher = new(_peerClient, _swarmApi, _peerService); + deltaHeightWatcher.Start(); + + var deltaIndex = await deltaHeightWatcher.WaitForDeltaIndexAsync(TimeSpan.FromSeconds(10)); + + deltaIndex.Height.Should().Be(deltaHeight); + } + } +} diff --git a/src/Catalyst.Core.Modules.Sync.Tests/UnitTests/PeerSyncManagerUnitTests.cs b/src/Catalyst.Core.Modules.Sync.Tests/UnitTests/PeerSyncManagerUnitTests.cs new file mode 100644 index 0000000000..e352d3070b --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync.Tests/UnitTests/PeerSyncManagerUnitTests.cs @@ -0,0 +1,60 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.P2P; +using Catalyst.Core.Lib.P2P.Models; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Core.Modules.Hashing; +using Catalyst.Protocol.Wire; +using MultiFormats.Registry; +using NSubstitute; +using SharpRepository.InMemoryRepository; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using Catalyst.Core.Lib.P2P.Repository; +using NUnit.Framework; + +namespace Catalyst.Core.Modules.Sync.Tests.UnitTests +{ + public class PeerSyncManagerUnitTests + { + private IPeerClient _peerClient; + private IHashProvider _hashProvider; + private IPeerService _peerService; + private IPeerRepository _peerRepository; + private ReplaySubject _deltaHeightReplaySubject; + + //todo add unit tests + [SetUp] + public void Init() + { + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + _peerService = Substitute.For(); + _peerClient = Substitute.For(); + _peerRepository = new PeerRepository(new InMemoryRepository()); + _deltaHeightReplaySubject = new ReplaySubject(1); + _peerService.MessageStream.Returns(_deltaHeightReplaySubject.AsObservable()); + } + } +} diff --git a/src/Catalyst.Core.Modules.Sync.Tests/UnitTests/SyncUnitTests.cs b/src/Catalyst.Core.Modules.Sync.Tests/UnitTests/SyncUnitTests.cs new file mode 100644 index 0000000000..95350e9567 --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync.Tests/UnitTests/SyncUnitTests.cs @@ -0,0 +1,443 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Cli; +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.Sync.Interfaces; +using Catalyst.Core.Abstractions.Sync; +using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Core.Lib.Service; +using Catalyst.Core.Modules.Consensus.Deltas; +using Catalyst.Core.Modules.Dfs.Extensions; +using Catalyst.Core.Modules.Hashing; +using Catalyst.Core.Modules.Sync.Manager; +using Catalyst.Core.Modules.Sync.Watcher; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.IPPN; +using Catalyst.Protocol.Wire; +using Catalyst.TestUtils; +using LibP2P = Lib.P2P; +using FluentAssertions; +using Google.Protobuf; +using Lib.P2P; +using MultiFormats.Registry; +using NSubstitute; +using SharpRepository.InMemoryRepository; +using NUnit.Framework; +using MultiFormats; +using Catalyst.Abstractions.Dfs.CoreApi; +using System.Reactive.Concurrency; +using NUnit.Framework.Internal; +using ILogger = Serilog.ILogger; + +namespace Catalyst.Core.Modules.Sync.Tests.UnitTests +{ + public class SyncUnitTests + { + private IHashProvider _hashProvider; + private IPeerSettings _peerSettings; + private IPeerClient _peerClient; + private IDeltaIndexService _deltaIndexService; + + private ReplaySubject _deltaHeightReplaySubject; + + private ReplaySubject _deltaHistoryReplaySubject; + + private IMapperProvider _mapperProvider; + private IUserOutput _userOutput; + + private IDeltaCache _deltaCache; + + private IPeerSyncManager _peerSyncManager; + private IDeltaHeightWatcher _deltaHeightWatcher; + + private IDeltaHashProvider _deltaHashProvider; + + private int _syncTestHeight = 1005; + + private ManualResetEventSlim _manualResetEventSlim; + + private CancellationToken _cancellationToken; + + [SetUp] + public void Init() + { + var peerService = Substitute.For(); + + List peers = new(); + Enumerable.Range(0, 5).Select(x => MultiAddressHelper.GetAddress(x.ToString(), port: x)).Select(x => new LibP2P.Peer { Id = x.PeerId, ConnectedAddress = x }).ToList().ForEach(peers.Add); + + var swarmApi = Substitute.For(); + swarmApi.PeersAsync().Returns(peers); + + _cancellationToken = new CancellationToken(); + + _manualResetEventSlim = new ManualResetEventSlim(false); + + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); + + _peerSettings = Substitute.For(); + _peerSettings.Address.Returns(MultiAddressHelper.GetAddress()); + + _deltaCache = Substitute.For(); + _deltaCache.GenesisHash.Returns("bafk2bzacecji5gcdd6lxsoazgnbg46c3vttjwwkptiw27enachziizhhkir2w".ToCid()); + + _deltaHashProvider = new DeltaHashProvider(_deltaCache, Substitute.For(), Substitute.For()); + + _deltaIndexService = new DeltaIndexService(new InMemoryRepository()); + _deltaIndexService.Add(new DeltaIndexDao { Cid = _hashProvider.ComputeUtf8MultiHash("0").ToCid(), Height = 0 }); + + _peerClient = Substitute.For(); + ModifyPeerClient((request, senderentifier) => + { + var deltaHeightResponse = new LatestDeltaHashResponse + { + DeltaIndex = new DeltaIndex + { + Cid = _hashProvider.ComputeUtf8MultiHash(_syncTestHeight.ToString()).ToCid().ToArray() + .ToByteString(), + Height = (uint) _syncTestHeight + } + }; + + _deltaHeightReplaySubject.OnNext(deltaHeightResponse.ToProtocolMessage(senderentifier, CorrelationId.GenerateCorrelationId())); + }); + + ModifyPeerClient((request, senderentifier) => + { + var data = GenerateSampleData((int) request.Height, (int) request.Range, (int) _syncTestHeight); + _deltaIndexService.Add(data.DeltaIndex.Select(x => DeltaIndexDao.ToDao(x, _mapperProvider))); + + _deltaHistoryReplaySubject.OnNext(data.ToProtocolMessage(senderentifier, CorrelationId.GenerateCorrelationId())); + }); + + _deltaHeightReplaySubject = new ReplaySubject(1); + _deltaHistoryReplaySubject = new ReplaySubject(1); + + var mergeMessageStreams = _deltaHeightReplaySubject.AsObservable() + .Merge(_deltaHistoryReplaySubject.AsObservable()); + + peerService.MessageStream.Returns(mergeMessageStreams); + + _deltaHashProvider = Substitute.For(); + _deltaHashProvider.TryUpdateLatestHash(Arg.Any(), Arg.Any()).Returns(true); + + _mapperProvider = new TestMapperProvider(); + + _userOutput = Substitute.For(); + + _deltaHeightWatcher = new DeltaHeightWatcher(_peerClient, swarmApi, peerService); + + var dfsService = Substitute.For(); + + _peerSyncManager = new PeerSyncManager(_peerClient, peerService, _userOutput, _deltaHeightWatcher, swarmApi, Substitute.For(), 0.7, 0); + } + + [TearDown] + public void TearDown() + { + _deltaIndexService.Dispose(); + _deltaHeightWatcher.Dispose(); + _peerSyncManager.Dispose(); + } + + private DeltaHistoryResponse GenerateSampleData(int height, int range, int maxHeight = -1) + { + DeltaHistoryResponse deltaHeightResponse = new(); + List deltaIndexList = new(); + var heightSum = height + range; + + if (heightSum > maxHeight) + { + heightSum = maxHeight; + } + + for (var i = height; i <= heightSum; i++) + { + deltaIndexList.Add(new DeltaIndex + { + Cid = ByteString.CopyFrom(_hashProvider.ComputeUtf8MultiHash(i.ToString()).ToCid().ToArray()), + Height = (uint) i + }); + } + + deltaHeightResponse.DeltaIndex.Add(deltaIndexList); + return deltaHeightResponse; + } + + private void ModifyPeerClient(Action callback) where TRequest : IMessage + { + _peerClient.When(x => + x.SendMessageToPeersAsync( + Arg.Is(y => y.Descriptor.ClrType.Name.EndsWith(typeof(TRequest).Name)), Arg.Any>())).Do( + z => + { + var request = (TRequest) z[0]; + var peers = (IEnumerable) z[1]; + foreach (var peer in peers) + { + callback.Invoke(request, peer); + } + }); + } + + [Test] + public async Task StartAsync_Should_Start_Sync() + { + _deltaHeightWatcher = Substitute.For(); + _deltaHeightWatcher.WaitForDeltaIndexAsync(Arg.Any(), Arg.Any()).Returns(new DeltaIndex { Cid = ByteString.Empty, Height = 10000 }); + + Synchroniser sync = new(new SyncState(), _peerSyncManager, _deltaCache, _deltaHeightWatcher, _deltaHashProvider, + _deltaIndexService, _mapperProvider, _userOutput, Substitute.For()); + + await sync.StartAsync(CancellationToken.None); + + sync.IsRunning.Should().BeTrue(); + } + + [Test] + public async Task StopAsync_Should_Stop_Sync() + { + _deltaHeightWatcher = Substitute.For(); + _deltaHeightWatcher.WaitForDeltaIndexAsync(Arg.Any(), Arg.Any()).Returns(new DeltaIndex { Cid = ByteString.Empty, Height = 10000 }); + + Synchroniser sync = new(new SyncState(), _peerSyncManager, _deltaCache, _deltaHeightWatcher, _deltaHashProvider, + _deltaIndexService, _mapperProvider, _userOutput, Substitute.For()); + + await sync.StartAsync(CancellationToken.None); + sync.IsRunning.Should().BeTrue(); + + await sync.StopAsync(CancellationToken.None); + sync.IsRunning.Should().BeFalse(); + } + + [Test] + public async Task StopAsync_Should_Log_If_Not_Running_Sync() + { + _deltaHeightWatcher = Substitute.For(); + _deltaHeightWatcher.GetHighestDeltaIndexAsync().Returns(new DeltaIndex { Cid = ByteString.Empty, Height = 10000 }); + + Synchroniser sync = new(new SyncState(), _peerSyncManager, _deltaCache, _deltaHeightWatcher, _deltaHashProvider, + _deltaIndexService, _mapperProvider, _userOutput, Substitute.For()); + + await sync.StopAsync(CancellationToken.None); + + _userOutput.Received(1).WriteLine("Sync is not currently running."); + + sync.IsRunning.Should().BeFalse(); + } + + [Test] + public async Task Can_Restore_DeltaIndex() + { + const int sampleCurrentDeltaHeight = 100; + + var cid = _hashProvider.ComputeUtf8MultiHash(sampleCurrentDeltaHeight.ToString()).ToCid(); + _deltaIndexService.Add(new DeltaIndexDao { Cid = cid, Height = sampleCurrentDeltaHeight }); + + Synchroniser sync = new(new SyncState(), _peerSyncManager, _deltaCache, Substitute.For(), _deltaHashProvider, + _deltaIndexService, _mapperProvider, _userOutput, Substitute.For()); + + await sync.StartAsync(CancellationToken.None); + + sync.CurrentHighestDeltaIndexStored.Should().Be(sampleCurrentDeltaHeight); + } + + [Test] + public async Task Sync_Can_Add_DeltaIndexRange_To_Repository() + { + _syncTestHeight = 10; + var expectedData = GenerateSampleData(0, _syncTestHeight, _syncTestHeight); + Synchroniser sync = new(new SyncState(), _peerSyncManager, _deltaCache, _deltaHeightWatcher, _deltaHashProvider, _deltaIndexService, + _mapperProvider, _userOutput, Substitute.For(), _syncTestHeight, 1, 30, Scheduler.Default); + + sync.SyncCompleted.Subscribe(x => { _manualResetEventSlim.Set(); }); + + await sync.StartAsync(CancellationToken.None); + + _manualResetEventSlim.Wait(); + + var range = _deltaIndexService.GetRange(0, (ulong) _syncTestHeight).Select(x => DeltaIndexDao.ToProtoBuff(x, _mapperProvider)); + range.Should().BeEquivalentTo(expectedData.DeltaIndex); + } + + [Test] + public async Task Sync_Can_Update_State() + { + _syncTestHeight = 10; + + Synchroniser sync = new(new SyncState(), _peerSyncManager, _deltaCache, _deltaHeightWatcher, _deltaHashProvider, _deltaIndexService, + _mapperProvider, _userOutput, Substitute.For(), _syncTestHeight, 1, 30, Scheduler.Default); + + sync.SyncCompleted.Subscribe(x => { _manualResetEventSlim.Set(); }); + + await sync.StartAsync(CancellationToken.None); + + _manualResetEventSlim.Wait(); + + _deltaHashProvider.Received(_syncTestHeight).TryUpdateLatestHash(Arg.Any(), Arg.Any()); + } + + [Test] + public async Task Sync_Can_Update_CurrentDeltaIndex_From_Requested_DeltaIndexRange() + { + _syncTestHeight = 10; + + Synchroniser sync = new(new SyncState(), _peerSyncManager, _deltaCache, _deltaHeightWatcher, _deltaHashProvider, _deltaIndexService, + _mapperProvider, _userOutput, Substitute.For()); + + sync.SyncCompleted.Subscribe(x => { _manualResetEventSlim.Set(); }); + + await sync.StartAsync(CancellationToken.None); + + _manualResetEventSlim.Wait(); + + sync.CurrentHighestDeltaIndexStored.Should().Be(10); + } + + [Test] + public async Task Sync_Can_Complete() + { + Synchroniser sync = new(new SyncState(), _peerSyncManager, _deltaCache, _deltaHeightWatcher, _deltaHashProvider, _deltaIndexService, + _mapperProvider, _userOutput, Substitute.For()); + + sync.SyncCompleted.Subscribe(x => { _manualResetEventSlim.Set(); }); + + await sync.StartAsync(CancellationToken.None); + + _manualResetEventSlim.Wait(); + + sync.CurrentHighestDeltaIndexStored.Should().Be((ulong) _syncTestHeight); + } + + private Dictionary BuildChainedDeltas(int chainSize) + { + var chainedDeltas = Enumerable.Range(0, chainSize + 1).ToDictionary( + i => _hashProvider.ComputeUtf8MultiHash(i.ToString()).ToCid(), + i => + { + var previousHash = _hashProvider.ComputeUtf8MultiHash((i - 1).ToString()).ToCid(); + var delta = DeltaHelper.GetDelta(_hashProvider, previousHash); + return delta; + }); + + _userOutput.WriteLine("chain is:"); + _userOutput.WriteLine(string.Join(Environment.NewLine, + chainedDeltas.Select((c, i) => + $"{i}: current {c.Key} | previous {c.Value.PreviousDeltaDfsHash.ToByteArray().ToCid()}"))); + return chainedDeltas; + } + + private void SetCacheExpectations(Dictionary deltasByHash) + { + foreach (var delta in deltasByHash) + { + _deltaCache.TryGetOrAddConfirmedDelta(delta.Key, out Arg.Any()) + .Returns(ci => + { + ci[1] = delta.Value; + return true; + }); + } + } + + [Test] + public void CacheDeltasBetween_Should_Stop_When_One_Of_Deltas_Is_Missing() + { + Synchroniser sync = new(new SyncState(), _peerSyncManager, _deltaCache, _deltaHeightWatcher, _deltaHashProvider, _deltaIndexService, + _mapperProvider, _userOutput, Substitute.For()); + + var chainSize = 5; + var chain = BuildChainedDeltas(chainSize); + SetCacheExpectations(chain); + + var hashes = chain.Keys.ToArray(); + var brokenChainIndex = 2; + _deltaCache.TryGetOrAddConfirmedDelta(hashes[brokenChainIndex], out Arg.Any()) + .Returns(false); + _userOutput.WriteLine($"chain is broken for {hashes[brokenChainIndex]}, it cannot be found on Dfs."); + + var cachedHashes = sync.CacheDeltasBetween(hashes.First(), + hashes.Last(), _cancellationToken).ToList(); + + OutputCachedHashes(cachedHashes); + + cachedHashes.Count.Should().Be(chainSize - brokenChainIndex); + hashes.TakeLast(chainSize - brokenChainIndex + 1).ToList().ForEach(h => + { + _deltaCache.Received(1).TryGetOrAddConfirmedDelta(h, + out Arg.Any(), _cancellationToken); + }); + } + + private void OutputCachedHashes(List cachedHashes) + { + _userOutput.WriteLine("cached hashes between: "); + _userOutput.WriteLine(string.Join(", ", cachedHashes)); + } + + [Test] + public void CacheDeltasBetween_Should_Complete_When_LatestKnownDelta_Is_Found() + { + Synchroniser sync = new(new SyncState(), _peerSyncManager, _deltaCache, _deltaHeightWatcher, _deltaHashProvider, _deltaIndexService, + _mapperProvider, _userOutput, Substitute.For()); + + var chainSize = 7; + var chain = BuildChainedDeltas(chainSize); + SetCacheExpectations(chain); + + var hashes = chain.Keys.ToArray(); + + var latestHashIndex = 3; + _userOutput.WriteLine($"Caching deltas between {hashes[latestHashIndex]} and {hashes.Last()}"); + var cachedHashes = sync.CacheDeltasBetween(hashes[latestHashIndex], + hashes.Last(), _cancellationToken).ToList(); + + var expectedResultLength = chainSize - latestHashIndex + 1; + cachedHashes.Count.Should().Be(expectedResultLength); + + OutputCachedHashes(cachedHashes); + + cachedHashes.Should().BeEquivalentTo(hashes.TakeLast(expectedResultLength)); + + hashes.TakeLast(expectedResultLength - 1).Reverse().ToList().ForEach(h => + { + _deltaCache.Received(1).TryGetOrAddConfirmedDelta(h, + out Arg.Any(), _cancellationToken); + }); + } + } +} diff --git a/src/Catalyst.Core.Modules.Sync/Catalyst.Core.Modules.Sync.csproj b/src/Catalyst.Core.Modules.Sync/Catalyst.Core.Modules.Sync.csproj new file mode 100644 index 0000000000..48122ac76d --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync/Catalyst.Core.Modules.Sync.csproj @@ -0,0 +1,24 @@ + + + net6.0 + Catalyst.Core.Modules.Sync + Darren Priestnall (darren.op@catalystnet.org) + + + + + + + + + + + + + + + + + + + diff --git a/src/Catalyst.Core.Modules.Sync/DeltaHeightRanker.cs b/src/Catalyst.Core.Modules.Sync/DeltaHeightRanker.cs new file mode 100644 index 0000000000..8659a6e184 --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync/DeltaHeightRanker.cs @@ -0,0 +1,114 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.Sync.Interfaces; +using Catalyst.Core.Modules.Sync.Modal; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.IPPN; +using MultiFormats; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; + +namespace Catalyst.Core.Modules.Sync +{ + public class DeltaHeightRanker : PeerMessageRankManager, IDeltaHeightRanker + { + private bool _disposed; + private readonly double _threshold; + private readonly int _maxPeersInStore; + private readonly ISwarmApi _swarmApi; + + private readonly ReplaySubject _foundDeltaHeightSubject; + public IObservable FoundDeltaHeight { get; } + + public DeltaHeightRanker(ISwarmApi swarmApi, int maxPeersInStore = 100, double threshold = 0.5, IScheduler scheduler = null) + { + _swarmApi = swarmApi; + _maxPeersInStore = maxPeersInStore; + _threshold = threshold; + + _foundDeltaHeightSubject = new ReplaySubject(1, scheduler ?? Scheduler.Default); + FoundDeltaHeight = _foundDeltaHeightSubject.AsObservable(); + } + + private int GetAvaliablePeerCount() => Math.Max(_maxPeersInStore, _swarmApi.PeersAsync().ConfigureAwait(false).GetAwaiter().GetResult().Count()); + public IEnumerable GetPeers() => _messages.Keys; + + public override void Add(MultiAddress key, LatestDeltaHashResponse value) + { + base.Add(key, value); + + var mostPopularMessages = GetMessagesByMostPopular(); + ClearPeersOutOfRange(mostPopularMessages); + + var minimumScore = GetAvaliablePeerCount() * _threshold; + if (mostPopularMessages.Where(x => x.Item.IsSync).Count() >= minimumScore || mostPopularMessages.Count() >= _maxPeersInStore) + { + _foundDeltaHeightSubject.OnNext(mostPopularMessages.First().Item.DeltaIndex); + } + } + + private void ClearPeersOutOfRange(IOrderedEnumerable> mostPopularMessages) + { + while (_maxPeersInStore < _messages.Count()) + { + var lastGroup = _messages.Where(kvp => kvp.Value == mostPopularMessages.Last().Item); + var lastItem = lastGroup.Last(); + _messages.Remove(lastItem); + } + } + + public int Count() + { + return _messages.Count(); + } + + public IOrderedEnumerable> GetMessagesByMostPopular() + { + return _messages.GroupBy(x => x.Value).Select(x => new RankedItem { Item = x.Key, Score = x.Count() }).OrderByDescending(x => x.Score).ThenByDescending(x => x.Item.DeltaIndex.Height); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _foundDeltaHeightSubject.Dispose(); + } + } + _disposed = true; + } + } +} diff --git a/src/Catalyst.Core.Modules.Sync/DeltaHistoryRanker.cs b/src/Catalyst.Core.Modules.Sync/DeltaHistoryRanker.cs new file mode 100644 index 0000000000..6b4bcfd809 --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync/DeltaHistoryRanker.cs @@ -0,0 +1,70 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.IPPN; +using Google.Protobuf.Collections; +using System.Linq; +using System.Threading; + +namespace Catalyst.Core.Modules.Sync +{ + public class DeltaHistoryRanker : PeerMessageRankManager, int> + { + private int _totalScore; + public int TotalScore => _totalScore; + + public int Height { set; get; } + public DeltaHistoryRequest DeltaHistoryRequest { private set; get; } + + public DeltaHistoryRanker(DeltaHistoryRequest deltaHistoryRequest) + { + DeltaHistoryRequest = deltaHistoryRequest; + Height = (int) deltaHistoryRequest.Height; + } + + public void Add(RepeatedField key) + { + Interlocked.Increment(ref _totalScore); + + if (_messages.ContainsKey(key)) + { + _messages[key]++; + return; + } + + _messages.Add(key, 1); + } + + public int GetHighestScore() + { + if (_messages.Count == 0) + { + return 0; + } + return _messages.Values.Max(x => x); + } + + public RepeatedField GetMostPopular() => _messages.Where(x => x.Value >= GetHighestScore()).Select(x=>x.Key)?.FirstOrDefault(); + } +} diff --git a/src/Catalyst.Core.Modules.Sync/Extensions/PeerClientExtensions.cs b/src/Catalyst.Core.Modules.Sync/Extensions/PeerClientExtensions.cs new file mode 100644 index 0000000000..ea0c2a7e88 --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync/Extensions/PeerClientExtensions.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.P2P; +using Catalyst.Core.Lib.Extensions; +using Google.Protobuf; +using MultiFormats; +using System.Collections.Generic; + +namespace Catalyst.Core.Modules.Sync.Extensions +{ + public static class PeerClientExtensions + { + public static void SendMessageToPeers(this IPeerClient peerClient, IPeerSettings peerSettings, IMessage message, IEnumerable peers) + { + var protocolMessage = message.ToProtocolMessage(peerSettings.Address); + foreach (var peer in peers) + { + peerClient.SendMessageAsync(protocolMessage, peer); + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Sync/Manager/PeerSyncManager.cs b/src/Catalyst.Core.Modules.Sync/Manager/PeerSyncManager.cs new file mode 100644 index 0000000000..c57c56a242 --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync/Manager/PeerSyncManager.cs @@ -0,0 +1,193 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Cli; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.Sync.Interfaces; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.IPPN; +using Serilog; + +namespace Catalyst.Core.Modules.Sync.Manager +{ + public class PeerSyncManager : IPeerSyncManager + { + private bool _isRunning; + private bool _disposed; + private readonly double _threshold; + private int _minimumPeers; + private readonly IPeerClient _peerClient; + private readonly IPeerService _peerService; + private readonly IUserOutput _userOutput; + private readonly ISwarmApi _swarmApi; + private readonly ReplaySubject> _scoredDeltaIndexRangeSubject; + + private IDisposable _deltaHistorySubscription; + + public IObservable> ScoredDeltaIndexRange { get; } + + public int MaxSyncPoolSize { get; } = 1; + + public DeltaHistoryRanker _deltaHistoryRanker; + + private readonly IDeltaHeightWatcher _deltaHeightWatcher; + + private readonly ILogger _logger; + + public PeerSyncManager(IPeerClient peerClient, + IPeerService peerService, + IUserOutput userOutput, + IDeltaHeightWatcher deltaHeightWatcher, + ISwarmApi swarmApi, + ILogger logger, + double threshold = 0.5d, + int minimumPeers = 0, + IScheduler scheduler = null) + { + _peerClient = peerClient; + _peerService = peerService; + _userOutput = userOutput; + _deltaHeightWatcher = deltaHeightWatcher; + _swarmApi = swarmApi; + _scoredDeltaIndexRangeSubject = + new ReplaySubject>(1, scheduler ?? Scheduler.Default); + ScoredDeltaIndexRange = _scoredDeltaIndexRangeSubject.AsObservable(); + + _logger = logger; + _threshold = threshold; + _minimumPeers = minimumPeers; + } + + public async Task WaitForPeersAsync(CancellationToken cancellationToken = default) + { + try + { + while (!cancellationToken.IsCancellationRequested) + { + var peers = await _swarmApi.PeersAsync().ConfigureAwait(false); + _userOutput.WriteLine($"Peers discovered for Sync: {peers.Count()}"); + await Task.Delay(1000, cancellationToken); + } + } + catch (TaskCanceledException) + { + //Task has been canceled. + } + } + + public void GetDeltaIndexRangeFromPeers(ulong index, int range) + { + var deltaHistoryRequest = new DeltaHistoryRequest + { Height = (uint) index, Range = (uint) range }; + + _deltaHistoryRanker = new DeltaHistoryRanker(deltaHistoryRequest); + } + + public void Start() + { + _isRunning = true; + _deltaHistorySubscription = _peerService.MessageStream.Where(x => x?.TypeUrl != null && + x.TypeUrl.EndsWith(typeof(DeltaHistoryResponse).ShortenedProtoFullName())) + .Select(x => x.FromProtocolMessage()).Subscribe(DeltaHistoryOnNext); + + Task.Factory.StartNew(SyncDeltaIndexes); + } + + public void Stop() => Dispose(); + + private async Task SyncDeltaIndexes() + { + while (_isRunning) + { + if (_deltaHistoryRanker != null) + { + var peers = await _swarmApi.PeersAsync().ConfigureAwait(false); + var messageCount = Math.Min(_minimumPeers, 50); + var minimumThreshold = messageCount * _threshold; + var score = _deltaHistoryRanker.GetHighestScore(); + if (score > minimumThreshold) + { + var deltaIndexes = _deltaHistoryRanker.GetMostPopular(); + _deltaHistoryRanker = null; + _scoredDeltaIndexRangeSubject.OnNext(deltaIndexes); + continue; + } + + _logger.Information($"Sync - Requesting delta range {_deltaHistoryRanker.DeltaHistoryRequest.Height} - {_deltaHistoryRanker.DeltaHistoryRequest.Height + _deltaHistoryRanker.DeltaHistoryRequest.Range}"); + await _peerClient.SendMessageToPeersAsync(_deltaHistoryRanker.DeltaHistoryRequest, peers.Select(x => x.ConnectedAddress)).ConfigureAwait(false); + } + await Task.Delay(2000); + } + } + + private void DeltaHistoryOnNext(DeltaHistoryResponse deltaHistoryResponse) + { + //First block is always previous block + if (deltaHistoryResponse.DeltaIndex.Count <= 1) + { + return; + } + + var deltaHistoryRanker = _deltaHistoryRanker; + if (deltaHistoryRanker == null) + { + return; + } + + var startHeight = (int) deltaHistoryResponse.DeltaIndex.FirstOrDefault()?.Height; + if (startHeight == deltaHistoryRanker.Height) + { + deltaHistoryRanker.Add(deltaHistoryResponse.DeltaIndex); + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _deltaHistorySubscription?.Dispose(); + } + } + _disposed = true; + _isRunning = false; + } + } +} diff --git a/src/Catalyst.Core.Modules.Sync/Modal/RankedItem.cs b/src/Catalyst.Core.Modules.Sync/Modal/RankedItem.cs new file mode 100644 index 0000000000..ee615fa56e --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync/Modal/RankedItem.cs @@ -0,0 +1,33 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Sync.Interfaces; + +namespace Catalyst.Core.Modules.Sync.Modal +{ + public class RankedItem : IRankedItem + { + public T Item { set; get; } + public int Score { set; get; } + } +} diff --git a/src/Catalyst.Abstractions/Mempool/Repositories/IMempoolRepository.cs b/src/Catalyst.Core.Modules.Sync/PeerMessageRankManager.cs similarity index 65% rename from src/Catalyst.Abstractions/Mempool/Repositories/IMempoolRepository.cs rename to src/Catalyst.Core.Modules.Sync/PeerMessageRankManager.cs index bc1e73d1af..fc3b978db0 100644 --- a/src/Catalyst.Abstractions/Mempool/Repositories/IMempoolRepository.cs +++ b/src/Catalyst.Core.Modules.Sync/PeerMessageRankManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,18 +21,17 @@ #endregion -using Catalyst.Abstractions.Repository; +using System.Collections.Concurrent; +using System.Collections.Generic; -namespace Catalyst.Abstractions.Mempool.Repositories +namespace Catalyst.Core.Modules.Sync { - public interface IMempoolRepository : IRepositoryWrapper where T : class + public abstract class PeerMessageRankManager { - bool TryReadItem(string signature); - - T ReadItem(string signature); - - bool DeleteItem(params string[] transactionSignatures); - - bool CreateItem(T transactionBroadcast); + protected IDictionary _messages = new ConcurrentDictionary(); + public virtual void Add(TKey key, TValue value) + { + _messages[key] = value; + } } } diff --git a/src/Catalyst.Core.Modules.Sync/Synchroniser.cs b/src/Catalyst.Core.Modules.Sync/Synchroniser.cs new file mode 100644 index 0000000000..cd756f282a --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync/Synchroniser.cs @@ -0,0 +1,306 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Cli; +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Sync.Interfaces; +using Catalyst.Core.Abstractions.Sync; +using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Lib.Service; +using Catalyst.Core.Modules.Dfs.Extensions; +using Catalyst.Protocol.Deltas; +using Serilog; +using Lib.P2P; + +namespace Catalyst.Core.Modules.Sync +{ + public class Synchroniser : ISynchroniser + { + public SyncState State { set; get; } + public bool IsRunning { private set; get; } + private bool _disposed; + private readonly int _rangeSize; + private readonly IUserOutput _userOutput; + private readonly IDeltaIndexService _deltaIndexService; + private readonly IMapperProvider _mapperProvider; + private readonly IDeltaHashProvider _deltaHashProvider; + private readonly IPeerSyncManager _peerSyncManager; + private readonly IDeltaHeightWatcher _deltaHeightWatcher; + private readonly ILogger _logger; + + private IDisposable _scoredDeltaIndexRangeDisposable; + + private Cid _previousHash; + + public ulong CurrentHighestDeltaIndexStored => _deltaIndexService.Height(); + public IDeltaCache DeltaCache { get; } + + public IObservable SyncCompleted { get; } + private readonly ReplaySubject _syncCompletedReplaySubject; + + private readonly int _peerDiscoveryDelay; + private readonly int _deltaHeightDelay; + + public Synchroniser(SyncState syncState, + IPeerSyncManager peerSyncManager, + IDeltaCache deltaCache, + IDeltaHeightWatcher deltaHeightWatcher, + IDeltaHashProvider deltaHashProvider, + IDeltaIndexService deltaIndexService, + IMapperProvider mapperProvider, + IUserOutput userOutput, + ILogger logger) : this(syncState, peerSyncManager, deltaCache, deltaHeightWatcher, deltaHashProvider, deltaIndexService, mapperProvider, userOutput, logger, 100, 10, 30, Scheduler.Default) + { } + + public Synchroniser(SyncState syncState, + IPeerSyncManager peerSyncManager, + IDeltaCache deltaCache, + IDeltaHeightWatcher deltaHeightWatcher, + IDeltaHashProvider deltaHashProvider, + IDeltaIndexService deltaIndexService, + IMapperProvider mapperProvider, + IUserOutput userOutput, + ILogger logger, + int rangeSize, + int peerDiscoveryDelay, + int deltaHeightDelay, + IScheduler scheduler) + { + State = syncState; + _peerSyncManager = peerSyncManager; + _deltaHeightWatcher = deltaHeightWatcher; + DeltaCache = deltaCache; + _rangeSize = rangeSize; + _deltaIndexService = deltaIndexService; + _mapperProvider = mapperProvider; + _userOutput = userOutput; + + _deltaHashProvider = deltaHashProvider; + + _logger = logger; + + _syncCompletedReplaySubject = new ReplaySubject(1, scheduler ?? Scheduler.Default); + SyncCompleted = _syncCompletedReplaySubject.AsObservable(); + + _peerDiscoveryDelay = peerDiscoveryDelay; + _deltaHeightDelay = deltaHeightDelay; + } + + public void UpdateState(ulong _latestKnownDeltaNumber) + { + State.CurrentBlock = _latestKnownDeltaNumber; + } + + /// + public IEnumerable CacheDeltasBetween(Cid latestKnownDeltaHash, + Cid targetDeltaHash, + CancellationToken cancellationToken) + { + var thisHash = targetDeltaHash; + + do + { + if (!DeltaCache.TryGetOrAddConfirmedDelta(thisHash, out var retrievedDelta, cancellationToken)) + { + yield break; + } + + var previousDfsHash = retrievedDelta.PreviousDeltaDfsHash.ToByteArray().ToCid(); + + _logger.Debug("Retrieved delta {previous} as predecessor of {current}", + previousDfsHash, thisHash); + + yield return thisHash; + + thisHash = previousDfsHash; + } while (!thisHash.Equals(latestKnownDeltaHash) + && !cancellationToken.IsCancellationRequested); + + yield return thisHash; + } + + public async Task StartAsync(CancellationToken cancellationToken = default) + { + if (IsRunning) + { + _userOutput.WriteLine("Sync is already running."); + return; + } + + IsRunning = true; + _previousHash = _deltaIndexService.LatestDeltaIndex().Cid; + _userOutput.WriteLine("Starting Sync..."); + + _scoredDeltaIndexRangeDisposable = _peerSyncManager.ScoredDeltaIndexRange.Subscribe(ProcessDeltaIndexRange); + + _deltaHeightWatcher.Start(); + + using (CancellationTokenSource WaitForPeersCancellationSource = new()) + { + using var cts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken, WaitForPeersCancellationSource.Token); + cts.CancelAfter(TimeSpan.FromSeconds(_peerDiscoveryDelay)); + await _peerSyncManager.WaitForPeersAsync(cts.Token).ConfigureAwait(false); + } + + var highestDeltaIndex = await _deltaHeightWatcher.WaitForDeltaIndexAsync(TimeSpan.FromSeconds(_deltaHeightDelay), cancellationToken).ConfigureAwait(false); + if (highestDeltaIndex == null || highestDeltaIndex.Height <= CurrentHighestDeltaIndexStored) + { + await Completed().ConfigureAwait(false); + return; + } + + State.CurrentBlock = State.StartingBlock = CurrentHighestDeltaIndexStored; + State.HighestBlock = highestDeltaIndex.Height; + + _peerSyncManager.Start(); + + Progress(CurrentHighestDeltaIndexStored, _rangeSize); + } + + private async void ProcessDeltaIndexRange(IEnumerable deltaIndexRange) + { + _logger.Information($"Sync - Processing delta index range."); + var deltaIndexRangeDao = deltaIndexRange.Select(x => + DeltaIndexDao.ToDao(x, _mapperProvider)).ToList(); + + var firstDeltaIndex = deltaIndexRangeDao.FirstOrDefault(); + if (firstDeltaIndex == null || firstDeltaIndex.Cid != _previousHash) + { + _logger.Error($"Sync Error - Previous delta({_previousHash}) does not match next delta({firstDeltaIndex.Cid})"); + return; + } + + deltaIndexRangeDao.Remove(firstDeltaIndex); + + _logger.Information($"Sync - Downloading deltas."); + DownloadDeltas(deltaIndexRangeDao); + + _logger.Information($"Sync - Updating state."); + UpdateState(deltaIndexRangeDao); + + _logger.Information($"Sync - Checking progress"); + await CheckSyncProgressAsync().ConfigureAwait(false); + + Progress(CurrentHighestDeltaIndexStored, _rangeSize); + } + + private void DownloadDeltas(IList deltaIndexes) + { + //Upgrade the DFS to batch download files using the wantlist rather then a task or thread for each file request. + } + + private async Task GetSyncProgressPercentageAsync() + { + var currentDeltaIndex = await _deltaHeightWatcher.GetHighestDeltaIndexAsync(); + var percentageSync = _deltaIndexService.Height() / currentDeltaIndex.Height * 100; + return (int) percentageSync; + } + + public Task StopAsync(CancellationToken cancellationToken = default) + { + if (!IsRunning) + { + _userOutput.WriteLine("Sync is not currently running."); + return Task.CompletedTask; + } + + _deltaHeightWatcher.Stop(); + _peerSyncManager.Stop(); + + _userOutput.WriteLine("Sync has been stopped"); + + IsRunning = false; + + return Task.CompletedTask; + } + + private void Progress(ulong index, int range) + { + if (!State.IsSynchronized) + { + _peerSyncManager.GetDeltaIndexRangeFromPeers(index, range); + } + } + + private void UpdateState(List deltaIndexes) => deltaIndexes.ForEach(x => + { + if (_deltaHashProvider.TryUpdateLatestHash(_previousHash, x.Cid)) + { + _previousHash = x.Cid; + } + }); + + private async Task CheckSyncProgressAsync() + { + var highestDeltaIndex = await _deltaHeightWatcher.GetHighestDeltaIndexAsync().ConfigureAwait(false); + State.CurrentBlock = CurrentHighestDeltaIndexStored; + State.HighestBlock = highestDeltaIndex.Height; + + if (CurrentHighestDeltaIndexStored >= highestDeltaIndex.Height) + { + _userOutput.WriteLine($"Sync Progress: { await GetSyncProgressPercentageAsync().ConfigureAwait(false)}%"); + await Completed().ConfigureAwait(false); + return true; + } + + _userOutput.WriteLine($"Sync Progress: {await GetSyncProgressPercentageAsync().ConfigureAwait(false)}%"); + return false; + } + + private async Task Completed() + { + State.IsSynchronized = true; + _syncCompletedReplaySubject.OnNext(CurrentHighestDeltaIndexStored); + await StopAsync().ConfigureAwait(false); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _scoredDeltaIndexRangeDisposable?.Dispose(); + _peerSyncManager?.Dispose(); + _deltaHeightWatcher?.Dispose(); + } + } + _disposed = true; + } + } +} diff --git a/src/Catalyst.Core.Modules.Sync/SynchroniserModule.cs b/src/Catalyst.Core.Modules.Sync/SynchroniserModule.cs new file mode 100644 index 0000000000..23424b6d05 --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync/SynchroniserModule.cs @@ -0,0 +1,50 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Autofac; +using Catalyst.Abstractions.Sync.Interfaces; +using Catalyst.Core.Abstractions.Sync; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Lib.Service; +using Catalyst.Core.Modules.Sync.Manager; +using Catalyst.Core.Modules.Sync.Watcher; +using SharpRepository.InMemoryRepository; +using SharpRepository.MongoDbRepository; +using SharpRepository.Repository; + +namespace Catalyst.Core.Modules.Sync +{ + public class SynchroniserModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + //builder.RegisterType>().As>().SingleInstance(); + builder.RegisterType>().As>().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + } + } +} diff --git a/src/Catalyst.Core.Modules.Sync/Watcher/DeltaHeightWatcher.cs b/src/Catalyst.Core.Modules.Sync/Watcher/DeltaHeightWatcher.cs new file mode 100644 index 0000000000..060eb3ddda --- /dev/null +++ b/src/Catalyst.Core.Modules.Sync/Watcher/DeltaHeightWatcher.cs @@ -0,0 +1,156 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using System.Reactive.Linq; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.Sync.Interfaces; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.IPPN; +using Catalyst.Protocol.Wire; +using Nethermind.Core.Extensions; + +namespace Catalyst.Core.Modules.Sync.Watcher +{ + public class DeltaHeightWatcher : IDeltaHeightWatcher + { + private bool _disposed; + public IDeltaHeightRanker DeltaHeightRanker { private set; get; } + private IDisposable _deltaHeightSubscription; + private readonly IPeerService _peerService; + private readonly IPeerClient _peerClient; + private readonly ISwarmApi _swarmApi; + + public DeltaIndex LatestDeltaHash { set; get; } + + private Timer _requestDeltaHeightTimer; + private readonly AutoResetEvent _autoResetEvent; + + public DeltaHeightWatcher(IPeerClient peerClient, + ISwarmApi swarmApi, + IPeerService peerService, + double threshold = 0.5d) + { + _peerClient = peerClient; + DeltaHeightRanker = new DeltaHeightRanker(swarmApi, 100, threshold); + _peerService = peerService; + _autoResetEvent = new AutoResetEvent(false); + _swarmApi = swarmApi; + } + + public async void RequestDeltaHeightTimerCallback(object state) + { + await RequestDeltaHeightFromPeers().ConfigureAwait(false); + } + + public async Task WaitForDeltaIndexAsync(TimeSpan timeout) + { + return await WaitForDeltaIndexAsync(timeout, CancellationToken.None).ConfigureAwait(false); + } + + public async Task WaitForDeltaIndexAsync(TimeSpan timeout, CancellationToken cancellationToken) + { + await _autoResetEvent.WaitOneAsync(timeout, cancellationToken); + return await GetHighestDeltaIndexAsync().ConfigureAwait(false); + } + + public Task GetHighestDeltaIndexAsync() + { + return Task.FromResult(GetMostPopularMessage()?.Item.DeltaIndex); + } + + private IRankedItem GetMostPopularMessage() + { + //Responses that have fully sync + var rankedResponses = DeltaHeightRanker.GetMessagesByMostPopular(); + var highestRankedSyncResponse = rankedResponses.Where(x => x.Item.IsSync).FirstOrDefault(); + if (highestRankedSyncResponse != null) + { + return highestRankedSyncResponse; + } + + //Responses that have not fully sync + var highestRankedUnSyncResponse = rankedResponses.FirstOrDefault(); + if (highestRankedUnSyncResponse != null) + { + return highestRankedUnSyncResponse; + } + + return null; + } + + private async Task RequestDeltaHeightFromPeers() + { + var connectedPeers = await _swarmApi.PeersAsync().ConfigureAwait(false); + await _peerClient.SendMessageToPeersAsync(new LatestDeltaHashRequest(), connectedPeers.Select(x => x.ConnectedAddress)).ConfigureAwait(false); + } + + private void DeltaHeightOnNext(ProtocolMessage protocolMessage) + { + var address = protocolMessage.Address; + var latestDeltaHash = protocolMessage.FromProtocolMessage(); + DeltaHeightRanker.Add(address, latestDeltaHash); + } + + public void Start() + { + _deltaHeightSubscription = _peerService.MessageStream.Where(x => x != null && + x.TypeUrl.EndsWith(typeof(LatestDeltaHashResponse).ShortenedProtoFullName())) + .Select(x => x) + .Subscribe(DeltaHeightOnNext); + + _requestDeltaHeightTimer = new Timer(RequestDeltaHeightTimerCallback, null, TimeSpan.Zero, TimeSpan.FromSeconds(10)); + } + + public void Stop() { CleanUp(); } + + private void CleanUp() + { + _deltaHeightSubscription?.Dispose(); + _requestDeltaHeightTimer?.Dispose(); + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + protected virtual void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + CleanUp(); + } + } + _disposed = true; + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3.Tests/Catalyst.Core.Modules.Web3.Tests.csproj b/src/Catalyst.Core.Modules.Web3.Tests/Catalyst.Core.Modules.Web3.Tests.csproj index ab6e9941f1..59a6adfc45 100644 --- a/src/Catalyst.Core.Modules.Web3.Tests/Catalyst.Core.Modules.Web3.Tests.csproj +++ b/src/Catalyst.Core.Modules.Web3.Tests/Catalyst.Core.Modules.Web3.Tests.csproj @@ -1,13 +1,23 @@ - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Web3.Client.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Web3.Tests.snk true + + 1701;1702;VSTHRD200;CS8002 + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + diff --git a/src/Catalyst.Core.Modules.Web3.Tests/UnitTests/ApiModuleTests.cs b/src/Catalyst.Core.Modules.Web3.Tests/UnitTests/ApiModuleTests.cs index 14193d354b..20e3d42d43 100644 --- a/src/Catalyst.Core.Modules.Web3.Tests/UnitTests/ApiModuleTests.cs +++ b/src/Catalyst.Core.Modules.Web3.Tests/UnitTests/ApiModuleTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,55 +28,46 @@ using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.Extensions.DependencyInjection; using Swashbuckle.AspNetCore.Swagger; -using Xunit; +using NUnit.Framework; +using System.Net; +using Catalyst.Core.Modules.Web3.Options; namespace Catalyst.Core.Modules.Web3.Client.Tests.UnitTests { public sealed class ApiModuleTests { - private ApiModule _apiModule; - private readonly IServiceCollection _serviceCollection; - - public ApiModuleTests() - { - _serviceCollection = new ServiceCollection(); + [Test] + public void Can_Add_Swagger() { + ServiceCollection serviceCollection = new(); + ApiModule apiModule = new(new HttpOptions(new IPEndPoint(IPAddress.Any, 5005)), new List(), true); + apiModule.ConfigureServices(serviceCollection); + serviceCollection.Any(service => service.ServiceType == typeof(ISwaggerProvider)).Should().Be(true); } - [Fact] - public void Can_Add_Swagger() { AssertService(typeof(ISwaggerProvider)); } - - [Fact] - public void Can_Not_Add_Swagger() { AssertService(typeof(ISwaggerProvider), false, false); } + [Test] + public void Can_Not_Add_Swagger() { + ServiceCollection serviceCollection = new(); + ApiModule apiModule = new(new HttpOptions(new IPEndPoint(IPAddress.Any, 5005)), new List(), false); + apiModule.ConfigureServices(serviceCollection); + serviceCollection.Any(service => service.ServiceType == typeof(ISwaggerProvider)).Should().Be(false); + } - [Fact] + [Test] public void Can_Add_Api() { Type[] serviceTypes = { typeof(ICorsService), - - // typeof(IViewCompilerProvider), - // typeof(RouteHandler) }; - Can_Add_Swagger(); + ServiceCollection serviceCollection = new(); + ApiModule apiModule = new(new HttpOptions(new IPEndPoint(IPAddress.Any, 5005)), new List(), true); + apiModule.ConfigureServices(serviceCollection); foreach (var serviceType in serviceTypes) { - AssertService(serviceType); - } - } - - private void AssertService(Type type, bool addSwagger = true, bool shouldContain = true) - { - if (_apiModule == null) - { - _apiModule = new ApiModule(null, - new List(), addSwagger); - _apiModule.ConfigureServices(_serviceCollection); + serviceCollection.Any(service => service.ServiceType == serviceType).Should().Be(true); } - - _serviceCollection.Any(service => service.ServiceType == type).Should().Be(shouldContain); } } } diff --git a/src/Catalyst.Core.Modules.Web3/ApiModule.cs b/src/Catalyst.Core.Modules.Web3/ApiModule.cs index 786994804f..a93d4fa189 100644 --- a/src/Catalyst.Core.Modules.Web3/ApiModule.cs +++ b/src/Catalyst.Core.Modules.Web3/ApiModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,39 +25,44 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Net; using System.Reflection; using Autofac; using Autofac.Extensions.DependencyInjection; -using Catalyst.Abstractions.Consensus.Deltas; -using Catalyst.Abstractions.Dfs; -using Catalyst.Abstractions.Ledger; -using Catalyst.Abstractions.Mempool; -using Catalyst.Abstractions.Mempool.Repositories; -using Catalyst.Core.Lib.DAO; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Kvm; using Catalyst.Core.Modules.Web3.Controllers.Handlers; +using Catalyst.Core.Modules.Web3.Options; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Server.Kestrel.Https; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.OpenApi.Models; -using Nethermind.Core; -using Nethermind.Core.Json; +using Nethermind.Serialization.Json; using Serilog; -using SharpRepository.Repository; using Module = Autofac.Module; namespace Catalyst.Core.Modules.Web3 { public sealed class ApiModule : Module { - private readonly string _apiBindingAddress; + private readonly HttpOptions _httpOptions; + private readonly HttpsOptions _httpsOptions; private readonly string[] _controllerModules; private IContainer _container; private readonly bool _addSwagger; + public ApiModule(HttpOptions httpOptions, List controllerModules, bool addSwagger = true) : this(httpOptions, null, controllerModules, addSwagger) + { } - public ApiModule(string apiBindingAddress, List controllerModules, bool addSwagger = true) + public ApiModule(HttpsOptions httpsOptions, List controllerModules, bool addSwagger = true) : this(null, httpsOptions, controllerModules, addSwagger) + { } + + public ApiModule(HttpOptions httpOptions, HttpsOptions httpsOptions, List controllerModules, bool addSwagger = true) { - _apiBindingAddress = apiBindingAddress; + _httpOptions = httpOptions; + _httpsOptions = httpsOptions; _controllerModules = controllerModules.ToArray(); _addSwagger = addSwagger; } @@ -68,15 +73,16 @@ protected override void Load(ContainerBuilder builder) var buildPath = Path.GetDirectoryName(executingAssembly); var webDirectory = Directory.CreateDirectory(Path.Combine(buildPath, "wwwroot")); - async void BuildCallback(IContainer container) + builder.RegisterBuildCallback(_container => { - _container = container; +// _container = container; var logger = _container.Resolve(); - + var certificateStore = _container.Resolve(); try { - var host = Host.CreateDefaultBuilder() + Host.CreateDefaultBuilder() .UseServiceProviderFactory(new AutofacServiceProviderFactory()) + .UseConsoleLifetime() .ConfigureContainer(ConfigureContainer) .ConfigureWebHostDefaults( webHostBuilder => @@ -84,53 +90,42 @@ async void BuildCallback(IContainer container) webHostBuilder .ConfigureServices(ConfigureServices) .Configure(Configure) - .UseUrls(_apiBindingAddress) .UseWebRoot(webDirectory.FullName) + .ConfigureKestrel(options => + { + if (_httpsOptions != null) + { + var certificate = certificateStore.ReadOrCreateCertificateFile(_httpsOptions.CertificateName); + options.Listen(_httpsOptions.BindingAddress, listenOptions => + { + listenOptions.UseHttps(certificate); + }); + options.ConfigureHttpsDefaults(o => o.ClientCertificateMode = ClientCertificateMode.RequireCertificate); + } + + if (_httpOptions != null) + { + options.Listen(_httpOptions.BindingAddress); + } + }) .UseSerilog(); - }).Build(); - await host.StartAsync(); + }).RunConsoleAsync(); + + //SIGINT is caught from kestrel because we are using RunConsoleAsync in HostBuilder, the SIGINT will not be received in the main console so we need to exit the process manually, to prevent needing to use two SIGINT's + // Environment.Exit(2); } catch (Exception e) { logger.Error(e, "Error loading API"); } - } - - builder.RegisterBuildCallback(BuildCallback); + }); base.Load(builder); } - //todo Clean up - find a better way to reuse the ContainerBuilder loaded into the module public void ConfigureContainer(ContainerBuilder builder) { builder.RegisterType().As().SingleInstance(); builder.RegisterType().As().SingleInstance(); - - //Mempool repo - builder.RegisterInstance(_container.Resolve>()) - .As>() - .SingleInstance(); - builder.RegisterInstance(_container.Resolve>()) - .As>() - .SingleInstance(); - builder.RegisterInstance(_container.Resolve>()) - .As>().SingleInstance(); - - builder.RegisterInstance(_container.Resolve()) - .As() - .SingleInstance(); - builder.RegisterInstance(_container.Resolve()) - .As() - .SingleInstance(); - builder.RegisterInstance(_container.Resolve()) - .As() - .SingleInstance(); - builder.RegisterInstance(_container.Resolve()) - .As() - .SingleInstance(); - builder.RegisterInstance(_container.Resolve()) - .As() - .SingleInstance(); } public void ConfigureServices(IServiceCollection services) @@ -143,7 +138,27 @@ public void ConfigureServices(IServiceCollection services) .AllowAnyHeader()); }); - services.AddMvcCore().AddNewtonsoftJson().AddApiExplorer(); + services.AddAntiforgery( + options => + { + options.Cookie.Name = "_af"; + options.Cookie.HttpOnly = true; + options.Cookie.SecurePolicy = CookieSecurePolicy.Always; + options.HeaderName = "X-XSRF-TOKEN"; + } + ); + + services.AddMvcCore().AddNewtonsoftJson(options => + { + var converters = options.SerializerSettings.Converters; + + converters.Add(new UInt256Converter()); + converters.Add(new NullableUInt256Converter()); + converters.Add(new KeccakConverter()); + converters.Add(new AddressConverter()); + converters.Add(new ByteArrayConverter()); + converters.Add(new CidJsonConverter()); + }).AddApiExplorer(); var mvcBuilder = services.AddRazorPages(); @@ -152,17 +167,16 @@ public void ConfigureServices(IServiceCollection services) mvcBuilder.AddControllersAsServices(); if (_addSwagger) - { services.AddSwaggerGen(swagger => { - swagger.SwaggerDoc("v1", new OpenApiInfo {Title = "Catalyst API", Description = "Catalyst"}); + swagger.SwaggerDoc("v1", new OpenApiInfo { Title = "Catalyst API", Description = "Catalyst" }); }); - } } public void Configure(IApplicationBuilder app) { - app.UseHttpsRedirection(); + //enable to force http to upgrade to https, disabled because dashboard uses http, wallet https. + //app.UseHttpsRedirection(); app.UseRouting(); app.UseDeveloperExceptionPage(); app.UseCors("AllowOrigin"); @@ -180,5 +194,31 @@ public void Configure(IApplicationBuilder app) app.UseSwaggerUI(swagger => { swagger.SwaggerEndpoint("/swagger/v1/swagger.json", "Catalyst API"); }); } } + + private sealed class SharedContainerProviderFactory : IServiceProviderFactory + { + readonly IContainer _container; + + public SharedContainerProviderFactory(IContainer container) + { + _container = container; + } + + public ContainerBuilder CreateBuilder(IServiceCollection services) + { + ContainerBuilder builder = new(); + builder.Populate(services); + return builder; + } + + public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder) + { + // using an obsolete way of updating an already created container + // TODO: TheNewAutonomy + // containerBuilder.Update(_container); + + return new AutofacServiceProvider(_container); + } + } } } diff --git a/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj b/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj index 80f7535fc8..42128afc31 100644 --- a/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj +++ b/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj @@ -1,12 +1,15 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Core.Modules.Web3 - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Core.Modules.Web3.snk true + + 1701;1702;CS8002 + @@ -15,14 +18,14 @@ - - - - - - - - - + + + + + + + + + diff --git a/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj.nuget.g.props b/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj.nuget.g.props deleted file mode 100644 index 20fbc3beca..0000000000 --- a/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj.nuget.g.props +++ /dev/null @@ -1,28 +0,0 @@ - - - - False - NuGet - /home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Modules.Web3/project.assets.json - /home/nsh/.nuget/packages/ - /home/nsh/.nuget/packages/ - PackageReference - 4.9.1 - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - - - - - - - /home/nsh/.nuget/packages/microsoft.entityframeworkcore.tools/2.1.0 - /home/nsh/.nuget/packages/microsoft.codeanalysis.analyzers/1.1.0 - /home/nsh/.nuget/packages/microsoft.aspnetcore.razor.design/2.2.0 - - \ No newline at end of file diff --git a/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj.nuget.g.targets b/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj.nuget.g.targets deleted file mode 100644 index e8ae9a6cec..0000000000 --- a/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj.nuget.g.targets +++ /dev/null @@ -1,15 +0,0 @@ - - - - $(MSBuildAllProjects);$(MSBuildThisFileFullPath) - - - - - - - - - - - \ No newline at end of file diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/EthController.cs b/src/Catalyst.Core.Modules.Web3/Controllers/EthController.cs index 0f6846ebe3..a8db9149b8 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/EthController.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/EthController.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,10 @@ using Catalyst.Core.Modules.Web3.Controllers.Handlers; using Microsoft.AspNetCore.Mvc; using Nethermind.Core; +using Nethermind.Serialization.Json; +using Newtonsoft.Json.Linq; using Serilog; +using Serilog.Events; namespace Catalyst.Core.Modules.Web3.Controllers { @@ -35,9 +38,10 @@ namespace Catalyst.Core.Modules.Web3.Controllers [Route("api/[controller]/[action]")] public class EthController : Controller { + private static readonly object Lock = new object(); private readonly IWeb3EthApi _web3EthApi; - private readonly IWeb3HandlerResolver _handlerResolver; private readonly IJsonSerializer _jsonSerializer; + private readonly IWeb3HandlerResolver _handlerResolver; private readonly ILogger _logger = Log.Logger.ForContext(typeof(EthController)); public EthController(IWeb3EthApi web3EthApi, IWeb3HandlerResolver handlerResolver, IJsonSerializer jsonSerializer) @@ -46,19 +50,49 @@ public EthController(IWeb3EthApi web3EthApi, IWeb3HandlerResolver handlerResolve _handlerResolver = handlerResolver ?? throw new ArgumentNullException(nameof(handlerResolver)); _jsonSerializer = jsonSerializer ?? throw new ArgumentNullException(nameof(jsonSerializer)); } + + private JsonRpcResponse[] HandleManyRequests(JsonRpcRequest[] requests) + { + JsonRpcResponse[] responses = new JsonRpcResponse[requests.Length]; + for (int i = 0; i < requests.Length; i++) + { + responses[i] = HandleSingleRequest(requests[i]); + } + + return responses; + } [HttpPost] - public JsonRpcResponse Request([FromBody] JsonRpcRequest request) + public object Request([FromBody] object body) + { + JToken jToken = body as JToken; + if (jToken?.Type == JTokenType.Array) + { + JsonRpcRequest[] requests = jToken.ToObject(); + return HandleManyRequests(requests); + } + + JsonRpcRequest request = jToken?.ToObject(); + return HandleSingleRequest(request); + } + + private JsonRpcResponse HandleSingleRequest(JsonRpcRequest request) { - _logger.Information("ETH JSON RPC request {id} {method} {params}", request.Id, request.Method, request.Params); EthWeb3RequestHandlerBase handler = _handlerResolver.Resolve(request.Method, request.Params.Length); if (handler == null) { - return new JsonRpcErrorResponse {Result = null, Error = new Error {Code = (int) ErrorType.MethodNotFound, Data = $"{request.Method}", Message = "Method not found"}}; + JsonRpcErrorResponse errorResponse = new JsonRpcErrorResponse {Result = null, Error = new Error {Code = (int) ErrorType.MethodNotFound, Data = $"{request.Method}", Message = "Method not found"}}; + _logger.Error("Failed ETH JSON RPC request {id} {method} {params}", request.Id, request.Method, request.Params); + return errorResponse; } - object result = handler.Handle(request.Params, _web3EthApi, _jsonSerializer); - return new JsonRpcResponse(request, result); + lock (Lock) + { + object result = handler.Handle(request.Params, _web3EthApi, _jsonSerializer); + JsonRpcResponse response = new(request, result); + _logger.Information("ETH JSON RPC request {id} {method} {params} returned {result}", request.Id, request.Method, request.Params, result); + return response; + } } } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthBlockNumberHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthBlockNumberHandler.cs index 60bdde8321..2e95aee24e 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthBlockNumberHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthBlockNumberHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,8 +30,7 @@ public class EthBlockNumberHandler : EthWeb3RequestHandler { protected override long Handle(IWeb3EthApi api) { - // return api.DeltaResolver.Latest.Number; - return 1; + return api.DeltaResolver.LatestDeltaNumber; } } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthCallHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthCallHandler.cs new file mode 100644 index 0000000000..74c6bec5d0 --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthCallHandler.cs @@ -0,0 +1,45 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.Kvm.Models; +using Catalyst.Abstractions.Ledger; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "call")] + public class EthCallHandler : EthWeb3RequestHandler + { + protected override byte[] Handle(TransactionForRpc transactionCall, BlockParameter block, IWeb3EthApi api) + { + if (api.TryGetDeltaWithCid(block, out var deltaWithCid)) + { + var callOutputTracer = api.CallAndRestore(transactionCall, deltaWithCid); + + return callOutputTracer.ReturnValue; + } + + throw new InvalidOperationException($"Delta not found: '{block}'"); + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthChainIdHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthChainIdHandler.cs new file mode 100644 index 0000000000..5a3a8985ba --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthChainIdHandler.cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Ledger; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "chainId")] + public class EthChainIdHandler : EthWeb3RequestHandler + { + protected override string Handle(IWeb3EthApi api) + { + return string.Format("0x{0:X}", 2); + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthEstimateGasHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthEstimateGasHandler.cs index 31c1e59f21..e450a50e0e 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthEstimateGasHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthEstimateGasHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,9 +29,13 @@ namespace Catalyst.Core.Modules.Web3.Controllers.Handlers [EthWeb3RequestHandler("eth", "estimateGas")] public class EthEstimateGasHandler : EthWeb3RequestHandler { - protected override long Handle(TransactionForRpc param1, IWeb3EthApi api) + protected override long Handle(TransactionForRpc transactionCall, IWeb3EthApi api) { - throw new System.NotImplementedException(); + var deltaWithCid = api.GetLatestDeltaWithCid(); + + var callOutputTracer = api.CallAndRestore(transactionCall, deltaWithCid); + + return callOutputTracer.GasSpent; } } } diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBalanceHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBalanceHandler.cs index 48c7b52d05..489f751ff9 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBalanceHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBalanceHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,9 +22,11 @@ #endregion using Catalyst.Abstractions.Ledger; +using Catalyst.Core.Lib.Extensions; using Catalyst.Protocol.Deltas; using Nethermind.Core; using Nethermind.Dirichlet.Numerics; +using Address = Nethermind.Core.Address; namespace Catalyst.Core.Modules.Web3.Controllers.Handlers { @@ -33,12 +35,9 @@ public class EthGetBalanceHandler : EthWeb3RequestHandler { protected override UInt256 Handle(Address address, IWeb3EthApi api) { - // change to appropriate hash - Delta delta = api.DeltaResolver.Latest; - - // Keccak stateRoot = api.StateRootResolver.Resolve(delta.Hash); <-- we need a delta hash - // return api.StateReader.GetBalance(stateRoot, address); - return new UInt256(1000000); + Delta delta = api.GetLatestDeltaWithCid().Delta; + var stateRoot = delta.StateRoot.ToKeccak(); + return api.StateReader.GetBalance(stateRoot, address); } } } diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockByHashHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockByHashHandler.cs new file mode 100644 index 0000000000..4f762fe1b0 --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockByHashHandler.cs @@ -0,0 +1,91 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Linq; +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Kvm.Models; +using Catalyst.Abstractions.Ledger; +using Catalyst.Abstractions.Repository; +using Catalyst.Core.Lib.Extensions; +using Lib.P2P; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Dirichlet.Numerics; +using Address = Nethermind.Core.Address; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "getBlockByHash")] + public class EthGetBlockByHashHandler : EthWeb3RequestHandler + { + protected override BlockForRpc Handle(Keccak deltaHash, bool includeFullTxs, IWeb3EthApi api) + { + DeltaWithCid deltaWithCid = api.GetDeltaWithCid(deltaHash.ToCid()); + return BuildBlock(api, deltaWithCid, deltaWithCid.Delta.DeltaNumber, api.HashProvider, includeFullTxs); + } + + private static BlockForRpc BuildBlock(IWeb3EthApi api, DeltaWithCid deltaWithCid, long blockNumber, IHashProvider hashProvider, bool includeFullTxs) + { + var (delta, deltaHash) = deltaWithCid; + + Address author; + var firstCoinBaseEntry = delta.CoinbaseEntries.FirstOrDefault(); + if (firstCoinBaseEntry != null) + { + author = new Address(firstCoinBaseEntry.ReceiverKvmAddress.ToByteArray()); + } + else + { + author = Address.Zero; + } + + var nonce = new byte[8]; + BinaryPrimitives.WriteUInt64BigEndian(nonce, 42); + BlockForRpc blockForRpc = new BlockForRpc + { + ExtraData = new byte[0], + Miner = author, + Difficulty = 1, + Hash = deltaHash, + Number = blockNumber, + GasLimit = (long)delta.GasLimit, + GasUsed = delta.GasUsed, + Timestamp = new UInt256(delta.TimeStamp.Seconds), + ParentHash = blockNumber == 0 ? null : Cid.Read(delta.PreviousDeltaDfsHash.ToByteArray()), + StateRoot = delta.StateRoot.ToKeccak(), + ReceiptsRoot = Keccak.EmptyTreeHash, + TransactionsRoot = Keccak.EmptyTreeHash, + LogsBloom = Bloom.Empty, + MixHash = Keccak.Zero, + Nonce = nonce, + Uncles = new Keccak[0], + Transactions = includeFullTxs ? (IEnumerable)api.ToTransactionsForRpc(deltaWithCid) : delta.PublicEntries.Select(x => x.GetHash(hashProvider)) + }; + blockForRpc.TotalDifficulty = (UInt256)((long)blockForRpc.Difficulty * (blockNumber + 1)); + blockForRpc.Sha3Uncles = Keccak.OfAnEmptySequenceRlp; + return blockForRpc; + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockByNumberHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockByNumberHandler.cs index 96e69079bc..f5b7c98ae5 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockByNumberHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockByNumberHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,42 +22,106 @@ #endregion using System; +using System.Buffers.Binary; +using System.Collections.Generic; +using System.Linq; +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Kvm; using Catalyst.Abstractions.Kvm.Models; using Catalyst.Abstractions.Ledger; +using Catalyst.Abstractions.Repository; +using Catalyst.Core.Lib.Extensions; +using Lib.P2P; using Nethermind.Core; using Nethermind.Core.Crypto; using Nethermind.Dirichlet.Numerics; +using Address = Nethermind.Core.Address; namespace Catalyst.Core.Modules.Web3.Controllers.Handlers { [EthWeb3RequestHandler("eth", "getBlockByNumber")] - public class EthGetBlockByNumberHandler : EthWeb3RequestHandler + public class EthGetBlockByNumberHandler : EthWeb3RequestHandler { - protected override BlockForRpc Handle(BlockParameter address, IWeb3EthApi api) + protected override BlockForRpc Handle(BlockParameter block, bool includeTx, IWeb3EthApi api) { - // this needs some kind of delta repository that knows current delta and is also able to retrieve past deltas - throw new NotImplementedException(); + Cid deltaHash; + long blockNumber; + + IDeltaCache deltaCache = api.DeltaCache; + IDeltaResolver deltaResolver = api.DeltaResolver; + + switch (block.Type) + { + case BlockParameterType.Earliest: + deltaHash = deltaCache.GenesisHash; + blockNumber = 0; + break; + case BlockParameterType.Latest: + deltaHash = deltaResolver.LatestDelta; + blockNumber = deltaResolver.LatestDeltaNumber; + break; + case BlockParameterType.Pending: + deltaHash = deltaResolver.LatestDelta; + blockNumber = deltaResolver.LatestDeltaNumber; + break; + case BlockParameterType.BlockNumber: + blockNumber = block.BlockNumber.Value; + if (!deltaResolver.TryResolve(blockNumber, out deltaHash)) + { + return null; + } + + break; + default: + throw new ArgumentOutOfRangeException(); + } + + DeltaWithCid deltaWithCid = api.GetDeltaWithCid(deltaHash); + return BuildBlock(api, deltaWithCid, blockNumber, api.HashProvider, includeTx); } - private BlockForRpc GetBlockByNumber(long number) + private static BlockForRpc BuildBlock(IWeb3EthApi api, DeltaWithCid deltaWithCid, long blockNumber, IHashProvider hashProvider, bool includeFullTxs) { - // create form delta - BlockForRpc blockForRpc = new BlockForRpc(); - blockForRpc.Miner = Address.Zero; - blockForRpc.Difficulty = 1000000; - blockForRpc.Hash = Keccak.Compute(number.ToString()); - blockForRpc.Number = number; - blockForRpc.GasLimit = 10_000_000; - blockForRpc.GasUsed = 0; - blockForRpc.Timestamp = (UInt256) number; - blockForRpc.ParentHash = number == 0 ? Keccak.Zero : Keccak.Compute((number - 1).ToString()); - blockForRpc.StateRoot = Keccak.EmptyTreeHash; - blockForRpc.ReceiptsRoot = Keccak.EmptyTreeHash; - blockForRpc.TransactionsRoot = Keccak.EmptyTreeHash; - blockForRpc.LogsBloom = Bloom.Empty; - blockForRpc.TotalDifficulty = (UInt256) ((long) blockForRpc.Difficulty * number); + var (delta, deltaHash) = deltaWithCid; + + Address author; + var firstCoinBaseEntry = delta.CoinbaseEntries.FirstOrDefault(); + if (firstCoinBaseEntry != null) + { + author = new Address(firstCoinBaseEntry.ReceiverKvmAddress.ToByteArray()); + } + else + { + author = Address.Zero; + } + + var nonce = new byte[8]; + BinaryPrimitives.WriteUInt64BigEndian(nonce, 42); + + BlockForRpc blockForRpc = new BlockForRpc + { + ExtraData = new byte[0], + Miner = author, + Difficulty = 1, + Hash = deltaHash, + Number = blockNumber, + GasLimit = (long)delta.GasLimit, + GasUsed = delta.GasUsed, + Timestamp = new UInt256(delta.TimeStamp.Seconds), + ParentHash = blockNumber == 0 ? null : Cid.Read(delta.PreviousDeltaDfsHash.ToByteArray()), + StateRoot = delta.StateRoot.ToKeccak(), + ReceiptsRoot = Keccak.EmptyTreeHash, + TransactionsRoot = Keccak.EmptyTreeHash, + LogsBloom = Bloom.Empty, + MixHash = Keccak.Zero, + Nonce = nonce, + Uncles = new Keccak[0], + Transactions = includeFullTxs? (IEnumerable)api.ToTransactionsForRpc(deltaWithCid) : delta.PublicEntries.Select(x => x.GetHash(hashProvider)) + }; + blockForRpc.TotalDifficulty = (UInt256) ((long) blockForRpc.Difficulty * (blockNumber + 1)); blockForRpc.Sha3Uncles = Keccak.OfAnEmptySequenceRlp; return blockForRpc; } } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockTransactionCountByHash.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockTransactionCountByHash.cs new file mode 100644 index 0000000000..7e82310c7c --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockTransactionCountByHash.cs @@ -0,0 +1,44 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Ledger; +using Catalyst.Core.Lib.Extensions; +using Lib.P2P; +using Nethermind.Core.Crypto; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "getBlockTransactionCountByHash")] + public class EthGetBlockTransactionCountByHash : EthWeb3RequestHandler + { + protected override long? Handle(Keccak deltaHash, IWeb3EthApi api) + { + if (api.DeltaCache.TryGetOrAddConfirmedDelta(deltaHash.ToCid(), out var delta)) + { + return delta.PublicEntries.Count; + } + + return default; + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockTransactionCountByNumber.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockTransactionCountByNumber.cs new file mode 100644 index 0000000000..1d698b35ce --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetBlockTransactionCountByNumber.cs @@ -0,0 +1,42 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Kvm.Models; +using Catalyst.Abstractions.Ledger; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "getBlockTransactionCountByNumber")] + public class EthGetBlockTransactionCountByNumber : EthWeb3RequestHandler + { + protected override long? Handle(BlockParameter block, IWeb3EthApi api) + { + if (api.TryGetDeltaWithCid(block, out var delta)) + { + return delta.Delta.PublicEntries.Count; + } + + return default; + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetCodeHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetCodeHandler.cs index 6254ab3b49..0d63ab7496 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetCodeHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetCodeHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,16 +22,22 @@ #endregion using Catalyst.Abstractions.Ledger; -using Nethermind.Core; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Protocol.Deltas; +using Nethermind.Core.Crypto; +using Address = Nethermind.Core.Address; namespace Catalyst.Core.Modules.Web3.Controllers.Handlers { [EthWeb3RequestHandler("eth", "getCode")] public class EthGetCodeHandler : EthWeb3RequestHandler { - protected override byte[] Handle(Address param1, IWeb3EthApi api) + protected override byte[] Handle(Address address, IWeb3EthApi api) { - throw new System.NotImplementedException(); + Delta delta = api.GetLatestDeltaWithCid().Delta; + var stateRoot = delta.StateRoot.ToKeccak(); + byte[] byteCode = api.StateReader.GetCode(stateRoot, address); + return byteCode; } } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetStorageAtHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetStorageAtHandler.cs index 2590194a65..feeb8f8cfa 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetStorageAtHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetStorageAtHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,17 +21,35 @@ #endregion +using System; +using Catalyst.Abstractions.Kvm.Models; using Catalyst.Abstractions.Ledger; -using Nethermind.Core; +using Catalyst.Core.Lib.Extensions; +using Nethermind.Dirichlet.Numerics; +using Nethermind.State; +using Address = Nethermind.Core.Address; namespace Catalyst.Core.Modules.Web3.Controllers.Handlers { [EthWeb3RequestHandler("eth", "getStorageAt")] - public class EthGetStorageAtHandler : EthWeb3RequestHandler + public class EthGetStorageAtHandler : EthWeb3RequestHandler { - protected override byte[] Handle(Address param1, IWeb3EthApi api) + protected override byte[] Handle(Address address, UInt256 index, BlockParameter block, IWeb3EthApi api) { - throw new System.NotImplementedException(); + if (api.TryGetDeltaWithCid(block, out var deltaWithCid)) + { + var stateRoot = deltaWithCid.Delta.StateRoot.ToKeccak(); + + if (api.StateReader.GetAccount(stateRoot, address) == null) + { + return new byte[0]; + } + + api.StateProvider.StateRoot = stateRoot; + return api.StorageProvider.Get(new StorageCell(address, index)); + } + + throw new InvalidOperationException($"Delta not found: '{block}'"); } } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionByBlockHashAndIndex.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionByBlockHashAndIndex.cs new file mode 100644 index 0000000000..89f128631b --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionByBlockHashAndIndex.cs @@ -0,0 +1,42 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Kvm.Models; +using Catalyst.Abstractions.Ledger; +using Catalyst.Core.Lib.Extensions; +using Lib.P2P; +using Nethermind.Core.Crypto; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "getTransactionByBlockHashAndIndex")] + public class EthGetTransactionByBlockHashAndIndex : EthWeb3RequestHandler + { + protected override TransactionForRpc Handle(Keccak deltaHash, int positionIndex, IWeb3EthApi api) + { + var delta = api.GetDeltaWithCid(deltaHash.ToCid()); + + return api.ToTransactionForRpc(delta, positionIndex); + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionByBlockNumberAndIndex.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionByBlockNumberAndIndex.cs new file mode 100644 index 0000000000..19657afb80 --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionByBlockNumberAndIndex.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.Kvm.Models; +using Catalyst.Abstractions.Ledger; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "getTransactionByBlockNumberAndIndex")] + public class EthGetTransactionByBlockNumberAndIndex : EthWeb3RequestHandler + { + protected override TransactionForRpc Handle(BlockParameter block, int positionIndex, IWeb3EthApi api) + { + if (api.TryGetDeltaWithCid(block, out var delta)) + { + return api.ToTransactionForRpc(delta, positionIndex); + } + + return default; + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionCountHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionCountHandler.cs index e7947b5f80..b25545ea50 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionCountHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionCountHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,18 +21,29 @@ #endregion +using System; +using Catalyst.Abstractions.Kvm.Models; using Catalyst.Abstractions.Ledger; +using Catalyst.Core.Lib.Extensions; using Nethermind.Core; +using Nethermind.Core.Crypto; using Nethermind.Dirichlet.Numerics; namespace Catalyst.Core.Modules.Web3.Controllers.Handlers { [EthWeb3RequestHandler("eth", "getTransactionCount")] - public class EthGetTransactionsCountHandler : EthWeb3RequestHandler + public class EthGetTransactionsCountHandler : EthWeb3RequestHandler { - protected override UInt256 Handle(Address param1, IWeb3EthApi api) + protected override UInt256 Handle(Address address, BlockParameter block, IWeb3EthApi api) { - throw new System.NotImplementedException(); + if (api.TryGetDeltaWithCid(block, out var deltaWithCid)) + { + Keccak stateRoot = deltaWithCid.Delta.StateRoot.ToKeccak(); + Account account = api.StateReader.GetAccount(stateRoot, address); + return account?.Nonce ?? 0; + } + + throw new InvalidOperationException($"Delta not found: '{block}'"); } } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionReceiptHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionReceiptHandler.cs index 8d5724174a..ecd58a0c23 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionReceiptHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionReceiptHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,18 +21,50 @@ #endregion +using System; +using System.Linq; using Catalyst.Abstractions.Kvm.Models; using Catalyst.Abstractions.Ledger; +using Catalyst.Abstractions.Ledger.Models; +using Nethermind.Core; using Nethermind.Core.Crypto; namespace Catalyst.Core.Modules.Web3.Controllers.Handlers { - [EthWeb3RequestHandler("eth", "getCode")] + [EthWeb3RequestHandler("eth", "getTransactionReceipt")] public class EthGetTransactionReceiptHandler : EthWeb3RequestHandler { - protected override ReceiptForRpc Handle(Keccak param1, IWeb3EthApi api) + protected override ReceiptForRpc Handle(Keccak txHash, IWeb3EthApi api) { - throw new System.NotImplementedException(); + if (api == null) throw new ArgumentNullException(nameof(api)); + if (txHash == null) + { + return null; + } + + TransactionReceipt receipt = api.FindReceipt(txHash); + if (receipt == null) + { + return null; + } + + ReceiptForRpc receiptForRpc = new ReceiptForRpc + { + TransactionHash = txHash, + TransactionIndex = receipt.Index, + BlockHash = receipt.DeltaHash, + BlockNumber = receipt.DeltaNumber, + CumulativeGasUsed = receipt.GasUsedTotal, + GasUsed = receipt.GasUsed, + From = new Address(receipt.Sender), + To = receipt.Recipient == null ? null : new Address(receipt.Recipient), + ContractAddress = receipt.ContractAddress == null ? null : new Address(receipt.ContractAddress), + Logs = receipt.Logs.Select((l, idx) => new LogEntryForRpc(receipt, l, idx)).ToArray(), + LogsBloom = new Bloom(receipt.Logs), + Status = receipt.StatusCode, + }; + + return receiptForRpc; } } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionsByHashHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionsByHashHandler.cs new file mode 100644 index 0000000000..a2f0da0ed4 --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetTransactionsByHashHandler.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Kvm.Models; +using Catalyst.Abstractions.Ledger; +using Nethermind.Core.Crypto; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "getTransactionByHash")] + public class EthGetTransactionsByHashHandler : EthWeb3RequestHandler + { + protected override TransactionForRpc Handle(Keccak transactionHash, IWeb3EthApi api) + { + if (api.FindTransactionData(transactionHash, out var deltaHash, out var delta, out var index)) + { + return api.ToTransactionForRpc(new DeltaWithCid { Cid = deltaHash, Delta = delta }, index); + } + + return default; + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleByBlockHashAndIndex.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleByBlockHashAndIndex.cs new file mode 100644 index 0000000000..d97d5d7f89 --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleByBlockHashAndIndex.cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Kvm.Models; +using Catalyst.Abstractions.Ledger; +using Nethermind.Core.Crypto; +using Nethermind.Dirichlet.Numerics; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "getUncleByBlockHashAndIndex")] + public class EthGetUncleByBlockHashAndIndex : EthWeb3RequestHandler + { + protected override BlockForRpc Handle(Keccak blockHashData, UInt256 positionIndex, IWeb3EthApi api) => null; + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleByBlockNumberAndIndex.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleByBlockNumberAndIndex.cs new file mode 100644 index 0000000000..2d6050e083 --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleByBlockNumberAndIndex.cs @@ -0,0 +1,35 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Kvm.Models; +using Catalyst.Abstractions.Ledger; +using Nethermind.Dirichlet.Numerics; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "getUncleByBlockNumberAndIndex")] + public class EthGetUncleByBlockNumberAndIndex : EthWeb3RequestHandler + { + protected override BlockForRpc Handle(BlockParameter blockParameter, UInt256 positionIndex, IWeb3EthApi api) => null; + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleCountByBlockHash.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleCountByBlockHash.cs new file mode 100644 index 0000000000..dd92e8ac8a --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleCountByBlockHash.cs @@ -0,0 +1,35 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Ledger; +using Nethermind.Core.Crypto; +using Nethermind.Dirichlet.Numerics; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "getUncleCountByBlockHash")] + public class EthGetUncleCountByBlockHash : EthWeb3RequestHandler + { + protected override UInt256? Handle(Keccak keccak, IWeb3EthApi api) => UInt256.Zero; + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleCountByBlockNumber.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleCountByBlockNumber.cs new file mode 100644 index 0000000000..11731afd64 --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthGetUncleCountByBlockNumber.cs @@ -0,0 +1,34 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Ledger; +using Nethermind.Dirichlet.Numerics; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "getUncleCountByBlockNumber")] + public class EthGetUncleCountByBlockNumber : EthWeb3RequestHandler + { + protected override UInt256? Handle(IWeb3EthApi api) => UInt256.Zero; + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthPendingTransactionsHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthPendingTransactionsHandler.cs new file mode 100644 index 0000000000..1b35191a20 --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthPendingTransactionsHandler.cs @@ -0,0 +1,68 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Kvm.Models; +using Catalyst.Abstractions.Ledger; +using Catalyst.Abstractions.Repository; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Protocol.Transaction; +using Nethermind.Dirichlet.Numerics; +using System.Collections.Generic; +using System.Linq; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "pendingTransactions")] + public class EthPendingTransactionsHandler : EthWeb3RequestHandler> + { + protected override IEnumerable Handle(IWeb3EthApi api) + { + var transactions = api.GetPendingTransactions(); + return ConvertMempoolTransactions(api, transactions.ToList()); + } + + private IEnumerable ConvertMempoolTransactions(IWeb3EthApi api, IList publicEntries) + { + foreach (var publicEntry in publicEntries) + { + yield return new TransactionForRpc + { + GasPrice = publicEntry.GasPrice.ToUInt256(), + BlockHash = null, + BlockNumber = (UInt256)0x0, + Nonce = publicEntry.Nonce, + To = Web3EthApiExtensions.ToAddress(publicEntry.ReceiverAddress), + From = Web3EthApiExtensions.ToAddress(publicEntry.SenderAddress), + Value = publicEntry.Amount.ToUInt256(), + Hash = publicEntry.GetHash(api.HashProvider), + Data = publicEntry.Data.ToByteArray(), + R = new byte[0], + S = new byte[0], + V = UInt256.Zero, + Gas = publicEntry.GasLimit, + TransactionIndex = (UInt256)0x0 + }; + } + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthSendRawTransactionHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthSendRawTransactionHandler.cs index 61aea9d137..6ec91f6b82 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthSendRawTransactionHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthSendRawTransactionHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,17 +21,66 @@ #endregion +using System; +using System.IO; +using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.Ledger; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Protocol.Transaction; +using Catalyst.Protocol.Wire; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using Nethermind.Core; using Nethermind.Core.Crypto; +using Nethermind.Crypto; +using Nethermind.Dirichlet.Numerics; +using Nethermind.Logging; +using Nethermind.Serialization.Rlp; +using Nethermind.Specs; namespace Catalyst.Core.Modules.Web3.Controllers.Handlers { [EthWeb3RequestHandler("eth", "sendRawTransaction")] public class EthSendRawTransactionHandler : EthWeb3RequestHandler { - protected override Keccak Handle(byte[] param1, IWeb3EthApi api) + protected override Keccak Handle(byte[] transaction, IWeb3EthApi api) { - throw new System.NotImplementedException(); + PublicEntry publicEntry; + try + { + Transaction tx = Rlp.Decode(transaction); + EthereumEcdsa ecdsa = new(MainnetSpecProvider.Instance, LimboLogs.Instance); + tx.SenderAddress = ecdsa.RecoverAddress(tx, MainnetSpecProvider.IstanbulBlockNumber); + tx.Timestamp = (UInt256) DateTimeOffset.UtcNow.ToUnixTimeSeconds(); + publicEntry = new PublicEntry + { + Data = (tx.Data ?? tx.Init).ToByteString(), + GasLimit = (ulong) tx.GasLimit, + GasPrice = tx.GasPrice.ToUint256ByteString(), + Nonce = (ulong) tx.Nonce, + SenderAddress = tx.SenderAddress.Bytes.ToByteString(), + ReceiverAddress = tx.To?.Bytes.ToByteString() ?? ByteString.Empty, + Amount = tx.Value.ToUint256ByteString(), + Signature = new Protocol.Cryptography.Signature + { + RawBytes = ByteString.CopyFrom((byte) 1) + } + }; + } + catch + { + try + { + TransactionBroadcast transactionBroadcast = TransactionBroadcast.Parser.ParseFrom(transaction); + publicEntry = transactionBroadcast.PublicEntry; + } + catch (Exception) + { + throw new InvalidDataException($"Transaction data could not be deserialized into a {nameof(PublicEntry)}"); + } + } + + return api.SendTransaction(publicEntry); } } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthSendTransactionHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthSendTransactionHandler.cs index 5bff8a8c74..4be91b37d8 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthSendTransactionHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthSendTransactionHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,6 +23,7 @@ using Catalyst.Abstractions.Kvm.Models; using Catalyst.Abstractions.Ledger; +using Catalyst.Core.Lib.Extensions; using Nethermind.Core.Crypto; namespace Catalyst.Core.Modules.Web3.Controllers.Handlers @@ -30,9 +31,14 @@ namespace Catalyst.Core.Modules.Web3.Controllers.Handlers [EthWeb3RequestHandler("eth", "sendTransaction")] public class EthSendTransactionHandler : EthWeb3RequestHandler { - protected override Keccak Handle(TransactionForRpc param1, IWeb3EthApi api) + protected override Keccak Handle(TransactionForRpc transaction, IWeb3EthApi api) { - throw new System.NotImplementedException(); + var deltaWithCid = api.GetLatestDeltaWithCid(); + var parentDelta = deltaWithCid.Delta; + Keccak root = parentDelta.StateRoot.ToKeccak(); + var publicEntry = api.ToPublicEntry(transaction, root); + + return api.SendTransaction(publicEntry); } } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthSycningHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthSycningHandler.cs new file mode 100644 index 0000000000..a851514b5b --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthSycningHandler.cs @@ -0,0 +1,42 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Ledger; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("eth", "syncing")] + public class EthSycningHandler : EthWeb3RequestHandler + { + protected override object Handle(IWeb3EthApi api) + { + var syncState = api.SyncState; + return syncState.IsSynchronized?(object)false:new + { + StartingBlock = string.Format("0x{0:X}", syncState.StartingBlock), + CurrentBlock = string.Format("0x{0:X}", syncState.CurrentBlock), + HighestBlock = string.Format("0x{0:X}", syncState.HighestBlock), + }; + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthWeb3RequestHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthWeb3RequestHandler.cs index 70b912acbb..8dc57a8239 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthWeb3RequestHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthWeb3RequestHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,27 +24,35 @@ using System; using Catalyst.Abstractions.Kvm.Models; using Catalyst.Abstractions.Ledger; -using Nethermind.Core; +using Nethermind.Core.Attributes; +using Nethermind.Serialization.Json; namespace Catalyst.Core.Modules.Web3.Controllers.Handlers { public abstract class EthWeb3RequestHandlerBase { public abstract int ParametersCount { get; } - public abstract object Handle(string[] parameters, IWeb3EthApi api, IJsonSerializer serializer); + + public abstract object Handle(object[] parameters, IWeb3EthApi api, IJsonSerializer serializer); [Todo(Improve.MissingFunctionality, "Implement BlockParametersConverter")] - protected TParam Deserialize(string parameter, IJsonSerializer serializer) + protected TParam Deserialize(object parameter, IJsonSerializer serializer) { + if (parameter is bool) + { + return (TParam) Convert.ChangeType(parameter, typeof(TParam)); + } + // use BlockParamConverter instead... if (typeof(TParam) == typeof(BlockParameter)) { - BlockParameter blockParameter = new BlockParameter(); - blockParameter.FromJson(parameter); + BlockParameter blockParameter = BlockParameter.FromJson(parameter as string); return (TParam) Convert.ChangeType(blockParameter, typeof(TParam)); } + + var parameterString = parameter is string ? $"\"{parameter}\"" : parameter?.ToString(); - return serializer.Deserialize(parameter); + return parameterString == null ? default : serializer.Deserialize(parameterString); } } @@ -52,7 +60,7 @@ public abstract class EthWeb3RequestHandler : EthWeb3RequestHandlerBase { public override int ParametersCount => 0; - public override object Handle(string[] parameters, IWeb3EthApi api, IJsonSerializer serializer) + public override object Handle(object[] parameters, IWeb3EthApi api, IJsonSerializer serializer) { return Handle(api); } @@ -64,7 +72,7 @@ public abstract class EthWeb3RequestHandler : EthWeb3RequestHa { public override int ParametersCount => 1; - public override object Handle(string[] parameters, IWeb3EthApi api, IJsonSerializer serializer) + public override object Handle(object[] parameters, IWeb3EthApi api, IJsonSerializer serializer) { TParam1 param1 = Deserialize(parameters[0], serializer); return Handle(param1, api); @@ -75,13 +83,30 @@ public override object Handle(string[] parameters, IWeb3EthApi api, IJsonSeriali public abstract class EthWeb3RequestHandler : EthWeb3RequestHandlerBase { - public override object Handle(string[] parameters, IWeb3EthApi api, IJsonSerializer serializer) + public override int ParametersCount => 2; + + public override object Handle(object[] parameters, IWeb3EthApi api, IJsonSerializer serializer) { TParam1 param1 = Deserialize(parameters[0], serializer); - TParam2 param2 = Deserialize(parameters[0], serializer); + TParam2 param2 = Deserialize(parameters[1], serializer); return Handle(param1, param2, api); } - protected abstract TResult Handle(TParam1 param1, TParam2 param2, IWeb3EthApi api); + protected abstract TResult Handle(TParam1 address, TParam2 param2, IWeb3EthApi api); + } + + public abstract class EthWeb3RequestHandler : EthWeb3RequestHandlerBase + { + public override int ParametersCount => 3; + + public override object Handle(object[] parameters, IWeb3EthApi api, IJsonSerializer serializer) + { + TParam1 param1 = Deserialize(parameters[0], serializer); + TParam2 param2 = Deserialize(parameters[1], serializer); + TParam3 param3 = Deserialize(parameters[2], serializer); + return Handle(param1, param2, param3, api); + } + + protected abstract TResult Handle(TParam1 param1, TParam2 param2, TParam3 param3, IWeb3EthApi api); } } diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthWeb3RequestHandlerAttribute.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthWeb3RequestHandlerAttribute.cs index 15cf9de6d6..2044e96537 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthWeb3RequestHandlerAttribute.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/EthWeb3RequestHandlerAttribute.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,7 +25,7 @@ namespace Catalyst.Core.Modules.Web3.Controllers.Handlers { - public class EthWeb3RequestHandlerAttribute : Attribute + internal sealed class EthWeb3RequestHandlerAttribute : Attribute { private readonly string _module; private readonly string _method; diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/IWeb3HandlerResolver.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/IWeb3HandlerResolver.cs index c1ec29c771..ab14df14e8 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/IWeb3HandlerResolver.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/IWeb3HandlerResolver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/NetPeerCountHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/NetPeerCountHandler.cs new file mode 100644 index 0000000000..ac17721a5d --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/NetPeerCountHandler.cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Ledger; +using System.Linq; + +namespace Catalyst.Core.Modules.Web3.Controllers.Handlers +{ + [EthWeb3RequestHandler("net", "peerCount")] + public class NetPeerCountHandler : EthWeb3RequestHandler + { + protected override string Handle(IWeb3EthApi api) { + return string.Format("0x{0:X}", api.DfsService.SwarmApi.PeersAsync().ConfigureAwait(false).GetAwaiter().GetResult().Count()); + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/NetVersionHandler.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/NetVersionHandler.cs index 0f7c0a30e9..069eb59dc5 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/NetVersionHandler.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/NetVersionHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,15 +22,14 @@ #endregion using Catalyst.Abstractions.Ledger; +using Catalyst.Abstractions.Types; +using Catalyst.Protocol.Network; namespace Catalyst.Core.Modules.Web3.Controllers.Handlers { [EthWeb3RequestHandler("net", "version")] public class NetVersionHandler : EthWeb3RequestHandler { - protected override long Handle(IWeb3EthApi api) - { - throw new System.NotImplementedException(); - } + protected override long Handle(IWeb3EthApi api) { return (long) NetworkType.Devnet; } } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/Web3HandlerResolver.cs b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/Web3HandlerResolver.cs index 18f4518ee5..dafe3ca275 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/Web3HandlerResolver.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/Handlers/Web3HandlerResolver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -33,7 +33,7 @@ public class Web3HandlerResolver : IWeb3HandlerResolver { private readonly ILogger _logger = Log.Logger.ForContext(typeof(Web3HandlerResolver)); - private Dictionary _handlers = new Dictionary(StringComparer.InvariantCultureIgnoreCase); + private Dictionary _handlers = new(StringComparer.InvariantCultureIgnoreCase); public Web3HandlerResolver() { diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/LedgerController.cs b/src/Catalyst.Core.Modules.Web3/Controllers/LedgerController.cs index c667531ca1..bd04bb5087 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/LedgerController.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/LedgerController.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -40,14 +40,14 @@ namespace Catalyst.Core.Modules.Web3.Controllers public sealed class LedgerController : Controller { private readonly IDeltaHashProvider _deltaHashProvider; - private readonly IDfs _dfs; + private readonly IDfsService _dfsService; private readonly IMapperProvider _mapperProvider; private readonly ILogger _logger; - public LedgerController(IDeltaHashProvider deltaHashProvider, IDfs dfs, IMapperProvider mapperProvider, ILogger logger) + public LedgerController(IDeltaHashProvider deltaHashProvider, IDfsService dfsService, IMapperProvider mapperProvider, ILogger logger) { _deltaHashProvider = deltaHashProvider; - _dfs = dfs; + _dfsService = dfsService; _mapperProvider = mapperProvider; _logger = logger; } @@ -58,7 +58,7 @@ public async Task GetLatestDeltaAsync(DateTime? asOf) var latest = _deltaHashProvider.GetLatestDeltaHash(asOf?.ToUniversalTime()); try { - using (var fullContentStream = await _dfs.ReadAsync(latest).ConfigureAwait(false)) + await using (var fullContentStream = await _dfsService.UnixFsApi.ReadFileAsync(latest).ConfigureAwait(false)) { var contentBytes = await fullContentStream.ReadAllBytesAsync(CancellationToken.None) .ConfigureAwait(false); diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/MempoolController.cs b/src/Catalyst.Core.Modules.Web3/Controllers/MempoolController.cs index 7af63130f2..a030889773 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/MempoolController.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/MempoolController.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,8 +22,8 @@ #endregion using System.Linq; -using Catalyst.Abstractions.Mempool.Repositories; -using Catalyst.Core.Lib.DAO; +using Catalyst.Abstractions.Mempool.Services; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Mempool.Repositories; using Microsoft.AspNetCore.Mvc; @@ -36,24 +36,24 @@ namespace Catalyst.Core.Modules.Web3.Controllers [Route("api/[controller]/[action]")] public sealed class MempoolController : BaseController { - private readonly MempoolRepository _mempoolRepository; + private readonly MempoolService _mempoolService; - public MempoolController(IMempoolRepository mempoolRepository) + public MempoolController(IMempoolService mempoolService) { - _mempoolRepository = (MempoolRepository) mempoolRepository; + _mempoolService = (MempoolService) mempoolService; } [HttpGet("{id}")] - public TransactionBroadcastDao Get(string id) + public PublicEntryDao Get(string id) { id = id.ToLowerInvariant(); - return _mempoolRepository.ReadItem(id); + return _mempoolService.ReadItem(id); } [HttpGet] public JsonResult GetMempool() { - return Json(_mempoolRepository.GetAll(), new JsonSerializerSettings + return Json(_mempoolService.GetAll(), new JsonSerializerSettings { Converters = JsonConverterProviders.Converters.ToList() }); diff --git a/src/Catalyst.Core.Modules.Web3/Controllers/PeerController.cs b/src/Catalyst.Core.Modules.Web3/Controllers/PeerController.cs index 4d46002afd..02b05e0eba 100644 --- a/src/Catalyst.Core.Modules.Web3/Controllers/PeerController.cs +++ b/src/Catalyst.Core.Modules.Web3/Controllers/PeerController.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,7 +22,7 @@ #endregion using System.Linq; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.Util; using Microsoft.AspNetCore.Mvc; using Newtonsoft.Json; diff --git a/src/Catalyst.Core.Modules.Ledger/StateRootResolver.cs b/src/Catalyst.Core.Modules.Web3/Options/HttpOptions.cs similarity index 72% rename from src/Catalyst.Core.Modules.Ledger/StateRootResolver.cs rename to src/Catalyst.Core.Modules.Web3/Options/HttpOptions.cs index 753a5b7e52..fd399cfae6 100644 --- a/src/Catalyst.Core.Modules.Ledger/StateRootResolver.cs +++ b/src/Catalyst.Core.Modules.Web3/Options/HttpOptions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,16 +21,17 @@ #endregion -using Catalyst.Abstractions.Kvm; -using Nethermind.Core.Crypto; +using System.Net; -namespace Catalyst.Core.Modules.Ledger +namespace Catalyst.Core.Modules.Web3.Options { - public class StateRootResolver : IStateRootResolver + public class HttpOptions { - public Keccak Resolve(Keccak deltaHash) + public IPEndPoint BindingAddress { get; } + + public HttpOptions(IPEndPoint bindingAddress) { - throw new System.NotImplementedException(); + BindingAddress = bindingAddress; } } -} \ No newline at end of file +} diff --git a/src/Catalyst.Core.Modules.Web3/Options/HttpsOptions.cs b/src/Catalyst.Core.Modules.Web3/Options/HttpsOptions.cs new file mode 100644 index 0000000000..4688a20e95 --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Options/HttpsOptions.cs @@ -0,0 +1,37 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Net; + +namespace Catalyst.Core.Modules.Web3.Options +{ + public class HttpsOptions : HttpOptions + { + public string CertificateName { get; } + + public HttpsOptions(IPEndPoint bindingAddress, string certificateName) : base(bindingAddress) + { + CertificateName = certificateName; + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/Web3EthApiExtensions.cs b/src/Catalyst.Core.Modules.Web3/Web3EthApiExtensions.cs new file mode 100644 index 0000000000..8bfd77d0b1 --- /dev/null +++ b/src/Catalyst.Core.Modules.Web3/Web3EthApiExtensions.cs @@ -0,0 +1,223 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using Catalyst.Abstractions.Kvm.Models; +using Catalyst.Abstractions.Ledger; +using Catalyst.Abstractions.Repository; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Modules.Web3.Controllers.Handlers; +using Catalyst.Protocol.Deltas; +using Catalyst.Protocol.Transaction; +using Google.Protobuf; +using Google.Protobuf.WellKnownTypes; +using Lib.P2P; +using Nethermind.Core; +using Nethermind.Core.Crypto; +using Nethermind.Dirichlet.Numerics; +using Nethermind.Evm.Tracing; + +namespace Catalyst.Core.Modules.Web3 +{ + public static class Web3EthApiExtensions + { + public static DeltaWithCid GetLatestDeltaWithCid(this IWeb3EthApi api) + { + return GetDeltaWithCid(api, api.DeltaResolver.LatestDelta); + } + + public static DeltaWithCid GetDeltaWithCid(this IWeb3EthApi api, Cid cid) + { + if (!api.DeltaCache.TryGetOrAddConfirmedDelta(cid, out Delta delta)) + { + throw new Exception($"Delta not found '{cid}'"); + } + + return new DeltaWithCid + { + Delta = delta, + Cid = cid + }; + } + + public static bool TryGetDeltaWithCid(this IWeb3EthApi api, BlockParameter block, out DeltaWithCid delta) + { + Cid cid; + switch (block.Type) + { + case BlockParameterType.Earliest: + cid = api.DeltaCache.GenesisHash; + break; + case BlockParameterType.Latest: + cid = api.DeltaResolver.LatestDelta; + break; + case BlockParameterType.Pending: + cid = api.DeltaResolver.LatestDelta; + break; + case BlockParameterType.BlockNumber: + var blockNumber = block.BlockNumber.Value; + if (!api.DeltaResolver.TryResolve(blockNumber, out cid)) + { + delta = default; + return false; + } + + break; + default: + throw new ArgumentOutOfRangeException(); + } + + delta = api.GetDeltaWithCid(cid); + return true; + } + + public static PublicEntry ToPublicEntry(this IWeb3EthApi api, TransactionForRpc transactionCall, Keccak root) + { + return new PublicEntry + { + Nonce = (ulong)api.StateReader.GetNonce(root, transactionCall.From), + SenderAddress = transactionCall.From.Bytes.ToByteString(), + ReceiverAddress = transactionCall.To?.Bytes.ToByteString() ?? ByteString.Empty, + GasLimit = (ulong)transactionCall.Gas.GetValueOrDefault(), + GasPrice = transactionCall.GasPrice.GetValueOrDefault().ToUint256ByteString(), + Amount = transactionCall.Value.GetValueOrDefault().ToUint256ByteString(), + Data = transactionCall.Data?.ToByteString() ?? ByteString.Empty + }; + } + + public static TransactionForRpc ToTransactionForRpc(this IWeb3EthApi api, DeltaWithCid deltaWithCid, int transactionIndex) + { + var (delta, deltaCid) = deltaWithCid; + var publicEntry = delta.PublicEntries[transactionIndex]; + var deltaNumber = delta.DeltaNumber; + + return new TransactionForRpc + { + GasPrice = publicEntry.GasPrice.ToUInt256(), + BlockHash = deltaCid, + BlockNumber = (UInt256)deltaNumber, + Nonce = publicEntry.Nonce, + To = ToAddress(publicEntry.ReceiverAddress), + From = ToAddress(publicEntry.SenderAddress), + Value = publicEntry.Amount.ToUInt256(), + Hash = publicEntry.GetHash(api.HashProvider), + Data = publicEntry.Data.ToByteArray(), + R = new byte[0], + S = new byte[0], + V = UInt256.Zero, + Gas = publicEntry.GasLimit, + TransactionIndex = (UInt256)transactionIndex + }; + } + + public static IEnumerable ToTransactionsForRpc(this IWeb3EthApi api, DeltaWithCid deltaWithCid) + { + var (delta, deltaCid) = deltaWithCid; + var publicEntries = delta.PublicEntries; + var deltaNumber = delta.DeltaNumber; + + for (var i = 0; i < publicEntries.Count; i++) + { + var publicEntry = publicEntries[i]; + yield return new TransactionForRpc + { + GasPrice = publicEntry.GasPrice.ToUInt256(), + BlockHash = deltaCid, + BlockNumber = (UInt256)deltaNumber, + Nonce = publicEntry.Nonce, + To = ToAddress(publicEntry.ReceiverAddress), + From = ToAddress(publicEntry.SenderAddress), + Value = publicEntry.Amount.ToUInt256(), + Hash = publicEntry.GetHash(api.HashProvider), + Data = publicEntry.Data.ToByteArray(), + R = new byte[0], + S = new byte[0], + V = UInt256.Zero, + Gas = publicEntry.GasLimit, + TransactionIndex = (UInt256)i + }; + } + } + + public static Address ToAddress(ByteString address) + { + if (address == null || address.IsEmpty) + { + return null; + } + + return new Address(address.ToByteArray()); + } + + public static CallOutputTracer CallAndRestore(this IWeb3EthApi api, TransactionForRpc transactionCall, DeltaWithCid deltaWithCid) + { + var parentDelta = deltaWithCid.Delta; + Keccak root = parentDelta.StateRoot.ToKeccak(); + + if (transactionCall.Gas == null) + { + transactionCall.Gas = parentDelta.GasLimit; + } + + var publicEntry = api.ToPublicEntry(transactionCall, root); + + var newDelta = deltaWithCid.CreateOneOffDelta(publicEntry); + + CallOutputTracer callOutputTracer = new(); + + api.StateProvider.StateRoot = root; + api.Executor.CallAndReset(newDelta, callOutputTracer); + api.StateProvider.Reset(); + api.StorageProvider.Reset(); + return callOutputTracer; + } + } + + public struct DeltaWithCid + { + public Delta Delta; + public Cid Cid; + + /// + /// Creates a delta for one off execution. + /// + public Delta CreateOneOffDelta(PublicEntry publicEntry) + { + Delta newDelta = Delta.Clone(); + + newDelta.PreviousDeltaDfsHash = Cid.ToArray().ToByteString(); + newDelta.CoinbaseEntries.Clear(); + newDelta.ConfidentialEntries.Clear(); + newDelta.PublicEntries.Clear(); + newDelta.PublicEntries.Add(publicEntry); + return newDelta; + } + + public void Deconstruct(out Delta delta, out Cid cid) + { + delta = Delta; + cid = Cid; + } + } +} diff --git a/src/Catalyst.Core.Modules.Web3/project.assets.json b/src/Catalyst.Core.Modules.Web3/project.assets.json deleted file mode 100644 index 6f7af3dd94..0000000000 --- a/src/Catalyst.Core.Modules.Web3/project.assets.json +++ /dev/null @@ -1,15218 +0,0 @@ -{ - "version": 3, - "targets": { - ".NETCoreApp,Version=v2.2": { - "Autofac/4.8.1": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.0", - "System.ComponentModel": "4.0.1" - }, - "compile": { - "lib/netstandard1.1/Autofac.dll": {} - }, - "runtime": { - "lib/netstandard1.1/Autofac.dll": {} - } - }, - "Autofac.Configuration/4.1.0": { - "type": "package", - "dependencies": { - "Autofac": "4.0.1", - "Microsoft.Extensions.Configuration": "1.0.2" - }, - "compile": { - "lib/netstandard2.0/Autofac.Configuration.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Autofac.Configuration.dll": {} - } - }, - "Autofac.Extensions.DependencyInjection/4.3.1": { - "type": "package", - "dependencies": { - "Autofac": "4.6.1", - "Microsoft.Extensions.DependencyInjection.Abstractions": "1.1.0" - }, - "compile": { - "lib/netstandard2.0/Autofac.Extensions.DependencyInjection.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Autofac.Extensions.DependencyInjection.dll": {} - } - }, - "AutofacSerilogIntegration/2.0.0": { - "type": "package", - "dependencies": { - "Autofac": "4.0.0", - "NETStandard.Library": "1.6.0", - "Serilog": "2.0.0" - }, - "compile": { - "lib/netstandard1.1/AutofacSerilogIntegration.dll": {} - }, - "runtime": { - "lib/netstandard1.1/AutofacSerilogIntegration.dll": {} - } - }, - "BinaryEncoding/1.4.0": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.Buffers": "4.4.0" - }, - "compile": { - "lib/netstandard1.1/BinaryEncoding.dll": {} - }, - "runtime": { - "lib/netstandard1.1/BinaryEncoding.dll": {} - } - }, - "Common.Logging/3.4.1": { - "type": "package", - "dependencies": { - "Common.Logging.Core": "3.4.1", - "Microsoft.CSharp": "4.0.1", - "System.Collections": "4.0.11", - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.Reflection.TypeExtensions": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" - }, - "compile": { - "lib/netstandard1.3/Common.Logging.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Common.Logging.dll": {} - } - }, - "Common.Logging.Core/3.4.1": { - "type": "package", - "dependencies": { - "Microsoft.CSharp": "4.0.1" - }, - "compile": { - "lib/netstandard1.0/Common.Logging.Core.dll": {} - }, - "runtime": { - "lib/netstandard1.0/Common.Logging.Core.dll": {} - } - }, - "Dawn.Guard/1.9.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Dawn.Guard.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Dawn.Guard.dll": {} - } - }, - "DnsClient/1.2.0": { - "type": "package", - "dependencies": { - "System.Buffers": "4.4.0" - }, - "compile": { - "lib/netstandard2.0/DnsClient.dll": {} - }, - "runtime": { - "lib/netstandard2.0/DnsClient.dll": {} - } - }, - "DotNetty.Buffers/0.6.0": { - "type": "package", - "dependencies": { - "DotNetty.Common": "0.6.0", - "NETStandard.Library": "1.6.1", - "System.Diagnostics.Contracts": "4.3.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.2" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Buffers.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Buffers.dll": {} - } - }, - "DotNetty.Codecs/0.6.0": { - "type": "package", - "dependencies": { - "DotNetty.Buffers": "0.6.0", - "DotNetty.Common": "0.6.0", - "DotNetty.Transport": "0.6.0", - "NETStandard.Library": "1.6.1", - "System.Collections.Immutable": "1.5.0", - "System.Diagnostics.Contracts": "4.3.0" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Codecs.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Codecs.dll": {} - } - }, - "DotNetty.Codecs.Protobuf/0.6.0": { - "type": "package", - "dependencies": { - "DotNetty.Buffers": "0.6.0", - "DotNetty.Codecs": "0.6.0", - "DotNetty.Common": "0.6.0", - "DotNetty.Transport": "0.6.0", - "Google.Protobuf": "3.2.0", - "NETStandard.Library": "1.6.1", - "System.Diagnostics.Contracts": "4.3.0" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Codecs.Protobuf.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Codecs.Protobuf.dll": {} - } - }, - "DotNetty.Common/0.6.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Logging": "1.1.1", - "NETStandard.Library": "1.6.1", - "System.Diagnostics.Contracts": "4.3.0", - "System.Net.NetworkInformation": "4.3.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.2" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Common.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Common.dll": {} - } - }, - "DotNetty.Handlers/0.6.0": { - "type": "package", - "dependencies": { - "DotNetty.Buffers": "0.6.0", - "DotNetty.Codecs": "0.6.0", - "DotNetty.Common": "0.6.0", - "DotNetty.Transport": "0.6.0", - "NETStandard.Library": "1.6.1", - "System.Diagnostics.Contracts": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.Net.Security": "4.3.2" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Handlers.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Handlers.dll": {} - } - }, - "DotNetty.Transport/0.6.0": { - "type": "package", - "dependencies": { - "DotNetty.Buffers": "0.6.0", - "DotNetty.Common": "0.6.0", - "NETStandard.Library": "1.6.1", - "System.Diagnostics.Contracts": "4.3.0", - "System.Diagnostics.StackTrace": "4.3.0", - "System.Net.NameResolution": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Threading.Tasks.Extensions": "4.5.1" - }, - "compile": { - "lib/netstandard1.3/DotNetty.Transport.dll": {} - }, - "runtime": { - "lib/netstandard1.3/DotNetty.Transport.dll": {} - } - }, - "Extensions.Data.xxHash.core20/1.0.2.1": { - "type": "package", - "compile": { - "lib/netstandard2.0/Extensions.Data.xxHash.core20.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Extensions.Data.xxHash.core20.dll": {} - } - }, - "Google.Protobuf/3.9.1": { - "type": "package", - "dependencies": { - "System.Memory": "4.5.2" - }, - "compile": { - "lib/netstandard2.0/Google.Protobuf.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Google.Protobuf.dll": {} - } - }, - "Ipfs.Core/0.51.1": { - "type": "package", - "dependencies": { - "Common.Logging": "3.4.1", - "Common.Logging.Core": "3.4.1", - "Google.Protobuf": "3.7.0", - "Newtonsoft.Json": "12.0.1", - "Portable.BouncyCastle": "1.8.5", - "SimpleBase": "1.3.1" - }, - "compile": { - "lib/netstandard2.0/Ipfs.Core.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Ipfs.Core.dll": {} - } - }, - "Ipfs.Engine/0.9.1": { - "type": "package", - "dependencies": { - "Ipfs.Core": "0.51.1", - "Makaretu.Dns.Unicast": "0.9.0", - "Newtonsoft.Json": "12.0.1", - "Nito.AsyncEx.Coordination": "5.0.0", - "PeerTalk": "0.11.3", - "PeterO.Cbor": "3.1.0", - "Portable.BouncyCastle": "1.8.5", - "SharpZipLib": "1.0.0", - "protobuf-net": "2.4.0" - }, - "compile": { - "lib/netstandard2.0/Ipfs.Engine.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Ipfs.Engine.dll": {} - } - }, - "Ipfs.HttpGateway/0.4.0": { - "type": "package", - "dependencies": { - "Ipfs.Core": "0.51.1", - "Ipfs.Engine": "0.9.1" - }, - "compile": { - "lib/netcoreapp2.2/Ipfs.HttpGateway.Views.dll": {}, - "lib/netcoreapp2.2/Ipfs.HttpGateway.dll": {} - }, - "runtime": { - "lib/netcoreapp2.2/Ipfs.HttpGateway.Views.dll": {}, - "lib/netcoreapp2.2/Ipfs.HttpGateway.dll": {} - } - }, - "IPNetwork2/2.1.2": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1" - }, - "compile": { - "lib/netstandard1.3/System.Net.IPNetwork.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Net.IPNetwork.dll": {} - } - }, - "Makaretu.Dns/1.4.1": { - "type": "package", - "dependencies": { - "SimpleBase": "1.3.1" - }, - "compile": { - "lib/netstandard2.0/Makaretu.Dns.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Makaretu.Dns.dll": {} - } - }, - "Makaretu.Dns.Multicast/0.18.0": { - "type": "package", - "dependencies": { - "Common.Logging": "3.4.1", - "IPNetwork2": "2.1.2", - "Makaretu.Dns": "1.3.0", - "Tmds.LibC": "0.2.0" - }, - "compile": { - "lib/netstandard2.0/Makaretu.Dns.Multicast.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Makaretu.Dns.Multicast.dll": {} - } - }, - "Makaretu.Dns.Unicast/0.9.0": { - "type": "package", - "dependencies": { - "Common.Logging": "3.4.1", - "Makaretu.Dns": "1.4.1", - "Nito.AsyncEx": "5.0.0" - }, - "compile": { - "lib/netstandard2.0/Makaretu.Dns.Unicast.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Makaretu.Dns.Unicast.dll": {} - } - }, - "Makaretu.KBucket/0.5.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Makaretu.KBucket.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Makaretu.KBucket.dll": {} - } - }, - "Microsoft.AspNet.WebApi.Client/5.2.4": { - "type": "package", - "dependencies": { - "Newtonsoft.Json": "10.0.1", - "Newtonsoft.Json.Bson": "1.0.1" - }, - "compile": { - "lib/netstandard2.0/System.Net.Http.Formatting.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Net.Http.Formatting.dll": {} - } - }, - "Microsoft.AspNetCore/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Diagnostics": "2.2.0", - "Microsoft.AspNetCore.HostFiltering": "2.2.0", - "Microsoft.AspNetCore.Hosting": "2.2.0", - "Microsoft.AspNetCore.Routing": "2.2.0", - "Microsoft.AspNetCore.Server.IIS": "2.2.0", - "Microsoft.AspNetCore.Server.IISIntegration": "2.2.0", - "Microsoft.AspNetCore.Server.Kestrel": "2.2.0", - "Microsoft.AspNetCore.Server.Kestrel.Https": "2.2.0", - "Microsoft.Extensions.Configuration.CommandLine": "2.2.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.2.0", - "Microsoft.Extensions.Configuration.FileExtensions": "2.2.0", - "Microsoft.Extensions.Configuration.Json": "2.2.0", - "Microsoft.Extensions.Configuration.UserSecrets": "2.2.0", - "Microsoft.Extensions.Logging": "2.2.0", - "Microsoft.Extensions.Logging.Configuration": "2.2.0", - "Microsoft.Extensions.Logging.Console": "2.2.0", - "Microsoft.Extensions.Logging.Debug": "2.2.0", - "Microsoft.Extensions.Logging.EventSource": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.dll": {} - } - }, - "Microsoft.AspNetCore.Antiforgery/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.DataProtection": "2.2.0", - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.AspNetCore.WebUtilities": "2.2.0", - "Microsoft.Extensions.ObjectPool": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Antiforgery.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Antiforgery.dll": {} - } - }, - "Microsoft.AspNetCore.App/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNet.WebApi.Client": "[5.2.4]", - "Microsoft.AspNetCore": "[2.1.0]", - "Microsoft.AspNetCore.Antiforgery": "[2.1.0]", - "Microsoft.AspNetCore.Authentication": "[2.1.0]", - "Microsoft.AspNetCore.Authentication.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.Authentication.Cookies": "[2.1.0]", - "Microsoft.AspNetCore.Authentication.Core": "[2.1.0]", - "Microsoft.AspNetCore.Authentication.Facebook": "[2.1.0]", - "Microsoft.AspNetCore.Authentication.Google": "[2.1.0]", - "Microsoft.AspNetCore.Authentication.JwtBearer": "[2.1.0]", - "Microsoft.AspNetCore.Authentication.MicrosoftAccount": "[2.1.0]", - "Microsoft.AspNetCore.Authentication.OAuth": "[2.1.0]", - "Microsoft.AspNetCore.Authentication.OpenIdConnect": "[2.1.0]", - "Microsoft.AspNetCore.Authentication.Twitter": "[2.1.0]", - "Microsoft.AspNetCore.Authentication.WsFederation": "[2.1.0]", - "Microsoft.AspNetCore.Authorization": "[2.1.0]", - "Microsoft.AspNetCore.Authorization.Policy": "[2.1.0]", - "Microsoft.AspNetCore.Connections.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.CookiePolicy": "[2.1.0]", - "Microsoft.AspNetCore.Cors": "[2.1.0]", - "Microsoft.AspNetCore.Cryptography.Internal": "[2.1.0]", - "Microsoft.AspNetCore.Cryptography.KeyDerivation": "[2.1.0]", - "Microsoft.AspNetCore.DataProtection": "[2.1.0]", - "Microsoft.AspNetCore.DataProtection.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.DataProtection.Extensions": "[2.1.0]", - "Microsoft.AspNetCore.Diagnostics": "[2.1.0]", - "Microsoft.AspNetCore.Diagnostics.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore": "[2.1.0]", - "Microsoft.AspNetCore.HostFiltering": "[2.1.0]", - "Microsoft.AspNetCore.Hosting": "[2.1.0]", - "Microsoft.AspNetCore.Hosting.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.Hosting.Server.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.Html.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.Http": "[2.1.0]", - "Microsoft.AspNetCore.Http.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.Http.Connections": "[1.0.0]", - "Microsoft.AspNetCore.Http.Connections.Common": "[1.0.0]", - "Microsoft.AspNetCore.Http.Extensions": "[2.1.0]", - "Microsoft.AspNetCore.Http.Features": "[2.1.0]", - "Microsoft.AspNetCore.HttpOverrides": "[2.1.0]", - "Microsoft.AspNetCore.HttpsPolicy": "[2.1.0]", - "Microsoft.AspNetCore.Identity": "[2.1.0]", - "Microsoft.AspNetCore.Identity.EntityFrameworkCore": "[2.1.0]", - "Microsoft.AspNetCore.Identity.UI": "[2.1.0]", - "Microsoft.AspNetCore.JsonPatch": "[2.1.0]", - "Microsoft.AspNetCore.Localization": "[2.1.0]", - "Microsoft.AspNetCore.Localization.Routing": "[2.1.0]", - "Microsoft.AspNetCore.MiddlewareAnalysis": "[2.1.0]", - "Microsoft.AspNetCore.Mvc": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.Analyzers": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.ApiExplorer": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.Core": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.Cors": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.DataAnnotations": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.Formatters.Json": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.Formatters.Xml": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.Localization": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.Razor": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.Razor.Extensions": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.Razor.ViewCompilation": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.RazorPages": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.TagHelpers": "[2.1.0]", - "Microsoft.AspNetCore.Mvc.ViewFeatures": "[2.1.0]", - "Microsoft.AspNetCore.NodeServices": "[2.1.0]", - "Microsoft.AspNetCore.Owin": "[2.1.0]", - "Microsoft.AspNetCore.Razor": "[2.1.0]", - "Microsoft.AspNetCore.Razor.Design": "[2.1.0]", - "Microsoft.AspNetCore.Razor.Language": "[2.1.0]", - "Microsoft.AspNetCore.Razor.Runtime": "[2.1.0]", - "Microsoft.AspNetCore.ResponseCaching": "[2.1.0]", - "Microsoft.AspNetCore.ResponseCaching.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.ResponseCompression": "[2.1.0]", - "Microsoft.AspNetCore.Rewrite": "[2.1.0]", - "Microsoft.AspNetCore.Routing": "[2.1.0]", - "Microsoft.AspNetCore.Routing.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.Server.HttpSys": "[2.1.0]", - "Microsoft.AspNetCore.Server.IISIntegration": "[2.1.0]", - "Microsoft.AspNetCore.Server.Kestrel": "[2.1.0]", - "Microsoft.AspNetCore.Server.Kestrel.Core": "[2.1.0]", - "Microsoft.AspNetCore.Server.Kestrel.Https": "[2.1.0]", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions": "[2.1.0]", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets": "[2.1.0]", - "Microsoft.AspNetCore.Session": "[2.1.0]", - "Microsoft.AspNetCore.SignalR": "[1.0.0]", - "Microsoft.AspNetCore.SignalR.Common": "[1.0.0]", - "Microsoft.AspNetCore.SignalR.Core": "[1.0.0]", - "Microsoft.AspNetCore.SignalR.Protocols.Json": "[1.0.0]", - "Microsoft.AspNetCore.SpaServices": "[2.1.0]", - "Microsoft.AspNetCore.SpaServices.Extensions": "[2.1.0]", - "Microsoft.AspNetCore.StaticFiles": "[2.1.0]", - "Microsoft.AspNetCore.WebSockets": "[2.1.0]", - "Microsoft.AspNetCore.WebUtilities": "[2.1.0]", - "Microsoft.CodeAnalysis.Razor": "[2.1.0]", - "Microsoft.EntityFrameworkCore": "[2.1.0]", - "Microsoft.EntityFrameworkCore.Abstractions": "[2.1.0]", - "Microsoft.EntityFrameworkCore.Analyzers": "[2.1.0]", - "Microsoft.EntityFrameworkCore.Design": "[2.1.0]", - "Microsoft.EntityFrameworkCore.InMemory": "[2.1.0]", - "Microsoft.EntityFrameworkCore.Relational": "[2.1.0]", - "Microsoft.EntityFrameworkCore.SqlServer": "[2.1.0]", - "Microsoft.EntityFrameworkCore.Tools": "[2.1.0]", - "Microsoft.Extensions.Caching.Abstractions": "[2.1.0]", - "Microsoft.Extensions.Caching.Memory": "[2.1.0]", - "Microsoft.Extensions.Caching.SqlServer": "[2.1.0]", - "Microsoft.Extensions.Configuration": "[2.1.0]", - "Microsoft.Extensions.Configuration.Abstractions": "[2.1.0]", - "Microsoft.Extensions.Configuration.Binder": "[2.1.0]", - "Microsoft.Extensions.Configuration.CommandLine": "[2.1.0]", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "[2.1.0]", - "Microsoft.Extensions.Configuration.FileExtensions": "[2.1.0]", - "Microsoft.Extensions.Configuration.Ini": "[2.1.0]", - "Microsoft.Extensions.Configuration.Json": "[2.1.0]", - "Microsoft.Extensions.Configuration.KeyPerFile": "[2.1.0]", - "Microsoft.Extensions.Configuration.UserSecrets": "[2.1.0]", - "Microsoft.Extensions.Configuration.Xml": "[2.1.0]", - "Microsoft.Extensions.DependencyInjection": "[2.1.0]", - "Microsoft.Extensions.DependencyInjection.Abstractions": "[2.1.0]", - "Microsoft.Extensions.DiagnosticAdapter": "[2.1.0]", - "Microsoft.Extensions.FileProviders.Abstractions": "[2.1.0]", - "Microsoft.Extensions.FileProviders.Composite": "[2.1.0]", - "Microsoft.Extensions.FileProviders.Embedded": "[2.1.0]", - "Microsoft.Extensions.FileProviders.Physical": "[2.1.0]", - "Microsoft.Extensions.FileSystemGlobbing": "[2.1.0]", - "Microsoft.Extensions.Hosting": "[2.1.0]", - "Microsoft.Extensions.Hosting.Abstractions": "[2.1.0]", - "Microsoft.Extensions.Http": "[2.1.0]", - "Microsoft.Extensions.Identity.Core": "[2.1.0]", - "Microsoft.Extensions.Identity.Stores": "[2.1.0]", - "Microsoft.Extensions.Localization": "[2.1.0]", - "Microsoft.Extensions.Localization.Abstractions": "[2.1.0]", - "Microsoft.Extensions.Logging": "[2.1.0]", - "Microsoft.Extensions.Logging.Abstractions": "[2.1.0]", - "Microsoft.Extensions.Logging.Configuration": "[2.1.0]", - "Microsoft.Extensions.Logging.Console": "[2.1.0]", - "Microsoft.Extensions.Logging.Debug": "[2.1.0]", - "Microsoft.Extensions.Logging.EventSource": "[2.1.0]", - "Microsoft.Extensions.Logging.TraceSource": "[2.1.0]", - "Microsoft.Extensions.ObjectPool": "[2.1.0]", - "Microsoft.Extensions.Options": "[2.1.0]", - "Microsoft.Extensions.Options.ConfigurationExtensions": "[2.1.0]", - "Microsoft.Extensions.Primitives": "[2.1.0]", - "Microsoft.Extensions.WebEncoders": "[2.1.0]", - "Microsoft.Net.Http.Headers": "[2.1.0]" - }, - "compile": { - "lib/netcoreapp2.1/_._": {} - }, - "runtime": { - "lib/netcoreapp2.1/_._": {} - }, - "build": { - "build/netcoreapp2.1/Microsoft.AspNetCore.App.props": {}, - "build/netcoreapp2.1/Microsoft.AspNetCore.App.targets": {} - } - }, - "Microsoft.AspNetCore.Authentication/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.Core": "2.1.0", - "Microsoft.AspNetCore.DataProtection": "2.1.0", - "Microsoft.AspNetCore.Http": "2.1.0", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0", - "Microsoft.Extensions.Options": "2.1.0", - "Microsoft.Extensions.WebEncoders": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.dll": {} - } - }, - "Microsoft.AspNetCore.Authentication.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.Authentication.Cookies/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Cookies.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Cookies.dll": {} - } - }, - "Microsoft.AspNetCore.Authentication.Core/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http": "2.2.0", - "Microsoft.AspNetCore.Http.Extensions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Core.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Core.dll": {} - } - }, - "Microsoft.AspNetCore.Authentication.Facebook/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.OAuth": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Facebook.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Facebook.dll": {} - } - }, - "Microsoft.AspNetCore.Authentication.Google/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.OAuth": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Google.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Google.dll": {} - } - }, - "Microsoft.AspNetCore.Authentication.JwtBearer/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication": "2.1.0", - "Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll": {} - } - }, - "Microsoft.AspNetCore.Authentication.MicrosoftAccount/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.OAuth": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.MicrosoftAccount.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.MicrosoftAccount.dll": {} - } - }, - "Microsoft.AspNetCore.Authentication.OAuth/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication": "2.1.0", - "Newtonsoft.Json": "11.0.2" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.OAuth.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.OAuth.dll": {} - } - }, - "Microsoft.AspNetCore.Authentication.OpenIdConnect/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.OAuth": "2.1.0", - "Microsoft.IdentityModel.Protocols.OpenIdConnect": "5.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.OpenIdConnect.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.OpenIdConnect.dll": {} - } - }, - "Microsoft.AspNetCore.Authentication.Twitter/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.OAuth": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Twitter.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Twitter.dll": {} - } - }, - "Microsoft.AspNetCore.Authentication.WsFederation/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication": "2.1.0", - "Microsoft.IdentityModel.Protocols.WsFederation": "5.2.0", - "System.IdentityModel.Tokens.Jwt": "5.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.WsFederation.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.WsFederation.dll": {} - } - }, - "Microsoft.AspNetCore.Authorization/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.dll": {} - } - }, - "Microsoft.AspNetCore.Authorization.Policy/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Authorization": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.Policy.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.Policy.dll": {} - } - }, - "Microsoft.AspNetCore.Connections.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.2.0", - "System.IO.Pipelines": "4.5.2" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Connections.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Connections.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.CookiePolicy/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http": "2.1.0", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0", - "Microsoft.Extensions.Options": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.CookiePolicy.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.CookiePolicy.dll": {} - } - }, - "Microsoft.AspNetCore.Cors/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.Extensions.Configuration.Abstractions": "2.2.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Cors.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Cors.dll": {} - } - }, - "Microsoft.AspNetCore.Cryptography.Internal/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.Internal.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.Internal.dll": {} - } - }, - "Microsoft.AspNetCore.Cryptography.KeyDerivation/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Cryptography.Internal": "2.1.0" - }, - "compile": { - "lib/netcoreapp2.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.dll": {} - }, - "runtime": { - "lib/netcoreapp2.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.dll": {} - } - }, - "Microsoft.AspNetCore.DataProtection/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Cryptography.Internal": "2.2.0", - "Microsoft.AspNetCore.DataProtection.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Win32.Registry": "4.5.0", - "System.Security.Cryptography.Xml": "4.5.0", - "System.Security.Principal.Windows": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.dll": {} - } - }, - "Microsoft.AspNetCore.DataProtection.Abstractions/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.DataProtection.Extensions/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.DataProtection": "2.1.0", - "Microsoft.Extensions.DependencyInjection": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Extensions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Extensions.dll": {} - } - }, - "Microsoft.AspNetCore.Diagnostics/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Diagnostics.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.AspNetCore.WebUtilities": "2.2.0", - "Microsoft.Extensions.FileProviders.Physical": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "System.Diagnostics.DiagnosticSource": "4.5.0", - "System.Reflection.Metadata": "1.6.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.dll": {} - } - }, - "Microsoft.AspNetCore.Diagnostics.Abstractions/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0", - "Microsoft.EntityFrameworkCore.Relational": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.dll": {} - } - }, - "Microsoft.AspNetCore.HostFiltering/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http": "2.2.0", - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.HostFiltering.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.HostFiltering.dll": {} - } - }, - "Microsoft.AspNetCore.Hosting/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http": "2.2.0", - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.Extensions.Configuration": "2.2.0", - "Microsoft.Extensions.Configuration.EnvironmentVariables": "2.2.0", - "Microsoft.Extensions.Configuration.FileExtensions": "2.2.0", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.FileProviders.Physical": "2.2.0", - "Microsoft.Extensions.Hosting.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "System.Diagnostics.DiagnosticSource": "4.5.0", - "System.Reflection.Metadata": "1.6.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll": {} - } - }, - "Microsoft.AspNetCore.Hosting.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Server.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.Hosting.Abstractions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.Hosting.Server.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.2.0", - "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.Html.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "System.Text.Encodings.Web": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Html.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Html.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.Http/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.AspNetCore.WebUtilities": "2.2.0", - "Microsoft.Extensions.ObjectPool": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Net.Http.Headers": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll": {} - } - }, - "Microsoft.AspNetCore.Http.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Features": "2.2.0", - "System.Text.Encodings.Web": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.Http.Connections/1.0.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authorization.Policy": "2.1.0", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0", - "Microsoft.AspNetCore.Http": "2.1.0", - "Microsoft.AspNetCore.Http.Connections.Common": "1.0.0", - "Microsoft.AspNetCore.Routing": "2.1.0", - "Microsoft.AspNetCore.WebSockets": "2.1.0", - "Newtonsoft.Json": "11.0.2" - }, - "compile": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.Http.Connections.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.Http.Connections.dll": {} - } - }, - "Microsoft.AspNetCore.Http.Connections.Common/1.0.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Connections.Abstractions": "2.1.0", - "Newtonsoft.Json": "11.0.2", - "System.Buffers": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Connections.Common.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Connections.Common.dll": {} - } - }, - "Microsoft.AspNetCore.Http.Extensions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", - "Microsoft.Net.Http.Headers": "2.2.0", - "System.Buffers": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll": {} - } - }, - "Microsoft.AspNetCore.Http.Features/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll": {} - } - }, - "Microsoft.AspNetCore.HttpOverrides/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.HttpOverrides.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.HttpOverrides.dll": {} - } - }, - "Microsoft.AspNetCore.HttpsPolicy/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0", - "Microsoft.AspNetCore.Http": "2.1.0", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0", - "Microsoft.Extensions.Configuration.Binder": "2.1.0", - "Microsoft.Extensions.Options": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.HttpsPolicy.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.HttpsPolicy.dll": {} - } - }, - "Microsoft.AspNetCore.Identity/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.Cookies": "2.1.0", - "Microsoft.AspNetCore.Cryptography.KeyDerivation": "2.1.0", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0", - "Microsoft.Extensions.Identity.Core": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.dll": {} - } - }, - "Microsoft.AspNetCore.Identity.EntityFrameworkCore/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Identity": "2.1.0", - "Microsoft.EntityFrameworkCore.Relational": "2.1.0", - "Microsoft.Extensions.Identity.Stores": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll": {} - } - }, - "Microsoft.AspNetCore.Identity.UI/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Identity": "2.1.0", - "Microsoft.AspNetCore.Mvc": "2.1.0", - "Microsoft.AspNetCore.StaticFiles": "2.1.0", - "Microsoft.Extensions.FileProviders.Embedded": "2.1.0", - "Microsoft.Extensions.Identity.Stores": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.Views.dll": {}, - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.Views.dll": {}, - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.dll": {} - } - }, - "Microsoft.AspNetCore.JsonPatch/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.CSharp": "4.5.0", - "Newtonsoft.Json": "11.0.2" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.JsonPatch.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.JsonPatch.dll": {} - } - }, - "Microsoft.AspNetCore.Localization/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.Extensions.Localization.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Localization.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Localization.dll": {} - } - }, - "Microsoft.AspNetCore.Localization.Routing/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Localization": "2.1.0", - "Microsoft.AspNetCore.Routing.Abstractions": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Localization.Routing.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Localization.Routing.dll": {} - } - }, - "Microsoft.AspNetCore.MiddlewareAnalysis/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0", - "System.Diagnostics.DiagnosticSource": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.MiddlewareAnalysis.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.MiddlewareAnalysis.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Mvc.Analyzers": "2.2.0", - "Microsoft.AspNetCore.Mvc.ApiExplorer": "2.2.0", - "Microsoft.AspNetCore.Mvc.Cors": "2.2.0", - "Microsoft.AspNetCore.Mvc.DataAnnotations": "2.2.0", - "Microsoft.AspNetCore.Mvc.Formatters.Json": "2.2.0", - "Microsoft.AspNetCore.Mvc.Localization": "2.2.0", - "Microsoft.AspNetCore.Mvc.Razor.Extensions": "2.2.0", - "Microsoft.AspNetCore.Mvc.RazorPages": "2.2.0", - "Microsoft.AspNetCore.Mvc.TagHelpers": "2.2.0", - "Microsoft.AspNetCore.Mvc.ViewFeatures": "2.2.0", - "Microsoft.AspNetCore.Razor.Design": "2.2.0", - "Microsoft.Extensions.Caching.Memory": "2.2.0", - "Microsoft.Extensions.DependencyInjection": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Routing.Abstractions": "2.2.0", - "Microsoft.Net.Http.Headers": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.Analyzers/2.2.0": { - "type": "package" - }, - "Microsoft.AspNetCore.Mvc.ApiExplorer/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Mvc.Core": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ApiExplorer.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ApiExplorer.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.Core/2.2.5": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.Core": "2.2.0", - "Microsoft.AspNetCore.Authorization.Policy": "2.2.0", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http": "2.2.0", - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.AspNetCore.Mvc.Abstractions": "2.2.0", - "Microsoft.AspNetCore.ResponseCaching.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Routing": "2.2.0", - "Microsoft.AspNetCore.Routing.Abstractions": "2.2.0", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.DependencyModel": "2.1.0", - "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "System.Diagnostics.DiagnosticSource": "4.5.0", - "System.Threading.Tasks.Extensions": "4.5.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Core.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Core.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.Cors/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Cors": "2.2.0", - "Microsoft.AspNetCore.Mvc.Core": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Cors.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Cors.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.DataAnnotations/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Mvc.Core": "2.2.0", - "Microsoft.Extensions.Localization": "2.2.0", - "System.ComponentModel.Annotations": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.DataAnnotations.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.DataAnnotations.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.Formatters.Json/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.JsonPatch": "2.2.0", - "Microsoft.AspNetCore.Mvc.Core": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Json.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Json.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.Formatters.Xml/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Mvc.Core": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Xml.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Xml.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.Localization/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Localization": "2.2.0", - "Microsoft.AspNetCore.Mvc.Razor": "2.2.0", - "Microsoft.Extensions.DependencyInjection": "2.2.0", - "Microsoft.Extensions.Localization": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Localization.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Localization.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.Razor/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Mvc.Razor.Extensions": "2.2.0", - "Microsoft.AspNetCore.Mvc.ViewFeatures": "2.2.0", - "Microsoft.AspNetCore.Razor.Runtime": "2.2.0", - "Microsoft.CodeAnalysis.CSharp": "2.8.0", - "Microsoft.CodeAnalysis.Razor": "2.2.0", - "Microsoft.Extensions.Caching.Memory": "2.2.0", - "Microsoft.Extensions.FileProviders.Composite": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.Razor.Extensions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Razor.Language": "2.2.0", - "Microsoft.CodeAnalysis.Razor": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.dll": {} - }, - "build": { - "build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.props": {}, - "build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.targets": {} - } - }, - "Microsoft.AspNetCore.Mvc.Razor.ViewCompilation/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting": "2.1.0", - "Microsoft.AspNetCore.Mvc.RazorPages": "2.1.0" - }, - "build": { - "build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.targets": {} - } - }, - "Microsoft.AspNetCore.Mvc.RazorPages/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Mvc.Razor": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.RazorPages.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.RazorPages.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.TagHelpers/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Mvc.Razor": "2.2.0", - "Microsoft.AspNetCore.Razor.Runtime": "2.2.0", - "Microsoft.AspNetCore.Routing.Abstractions": "2.2.0", - "Microsoft.Extensions.Caching.Memory": "2.2.0", - "Microsoft.Extensions.FileSystemGlobbing": "2.2.0", - "Microsoft.Extensions.Primitives": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.TagHelpers.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.TagHelpers.dll": {} - } - }, - "Microsoft.AspNetCore.Mvc.ViewFeatures/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Antiforgery": "2.2.0", - "Microsoft.AspNetCore.Diagnostics.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Html.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Mvc.Core": "2.2.0", - "Microsoft.AspNetCore.Mvc.DataAnnotations": "2.2.0", - "Microsoft.AspNetCore.Mvc.Formatters.Json": "2.2.0", - "Microsoft.Extensions.WebEncoders": "2.2.0", - "Newtonsoft.Json.Bson": "1.0.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ViewFeatures.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ViewFeatures.dll": {} - } - }, - "Microsoft.AspNetCore.NodeServices/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0", - "Microsoft.Extensions.Logging.Console": "2.1.0", - "Newtonsoft.Json": "11.0.2" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.NodeServices.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.NodeServices.dll": {} - } - }, - "Microsoft.AspNetCore.Owin/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Owin.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Owin.dll": {} - } - }, - "Microsoft.AspNetCore.Razor/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Html.Abstractions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.dll": {} - } - }, - "Microsoft.AspNetCore.Razor.Design/2.2.0": { - "type": "package", - "build": { - "build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.props": {} - }, - "buildMultiTargeting": { - "buildMultiTargeting/Microsoft.AspNetCore.Razor.Design.props": {} - } - }, - "Microsoft.AspNetCore.Razor.Language/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Language.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Language.dll": {} - } - }, - "Microsoft.AspNetCore.Razor.Runtime/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Html.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Razor": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Runtime.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Runtime.dll": {} - } - }, - "Microsoft.AspNetCore.ResponseCaching/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http": "2.1.0", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0", - "Microsoft.AspNetCore.ResponseCaching.Abstractions": "2.1.0", - "Microsoft.Extensions.Caching.Memory": "2.1.0", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.dll": {} - } - }, - "Microsoft.AspNetCore.ResponseCaching.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.ResponseCompression/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.1.0", - "Microsoft.Extensions.Options": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCompression.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCompression.dll": {} - } - }, - "Microsoft.AspNetCore.Rewrite/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.1.0", - "Microsoft.AspNetCore.Http.Extensions": "2.1.0", - "Microsoft.Extensions.Configuration.Abstractions": "2.1.0", - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0", - "Microsoft.Extensions.Options": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Rewrite.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Rewrite.dll": {} - } - }, - "Microsoft.AspNetCore.Routing/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.AspNetCore.Routing.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.ObjectPool": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netcoreapp2.2/Microsoft.AspNetCore.Routing.dll": {} - }, - "runtime": { - "lib/netcoreapp2.2/Microsoft.AspNetCore.Routing.dll": {} - } - }, - "Microsoft.AspNetCore.Routing.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Routing.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Routing.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.Server.HttpSys/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.Core": "2.1.0", - "Microsoft.AspNetCore.Hosting": "2.1.0", - "Microsoft.Net.Http.Headers": "2.1.0", - "Microsoft.Win32.Registry": "4.5.0", - "System.Security.Principal.Windows": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.HttpSys.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.HttpSys.dll": {} - } - }, - "Microsoft.AspNetCore.Server.IIS/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.Core": "2.2.0", - "Microsoft.AspNetCore.Connections.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "System.IO.Pipelines": "4.5.2", - "System.Security.Principal.Windows": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.IIS.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.IIS.dll": {} - }, - "build": { - "build/netstandard2.0/Microsoft.AspNetCore.Server.IIS.targets": {} - }, - "runtimeTargets": { - "runtimes/win-x64/nativeassets/netcoreapp2.2/aspnetcorev2_inprocess.dll": { - "assetType": "native", - "rid": "win-x64" - }, - "runtimes/win-x86/nativeassets/netcoreapp2.2/aspnetcorev2_inprocess.dll": { - "assetType": "native", - "rid": "win-x86" - } - } - }, - "Microsoft.AspNetCore.Server.IISIntegration/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authentication.Core": "2.2.0", - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http": "2.2.0", - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.AspNetCore.HttpOverrides": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "System.Buffers": "4.5.0", - "System.IO.Pipelines": "4.5.2", - "System.Memory": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.1", - "System.Security.Principal.Windows": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.IISIntegration.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.IISIntegration.dll": {} - }, - "build": { - "build/netstandard2.0/Microsoft.AspNetCore.Server.IISIntegration.targets": {} - } - }, - "Microsoft.AspNetCore.Server.Kestrel/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting": "2.2.0", - "Microsoft.AspNetCore.Server.Kestrel.Core": "2.2.0", - "Microsoft.AspNetCore.Server.Kestrel.Https": "2.2.0", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.dll": {} - } - }, - "Microsoft.AspNetCore.Server.Kestrel.Core/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http": "2.2.0", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions": "2.2.0", - "Microsoft.AspNetCore.WebUtilities": "2.2.0", - "Microsoft.Extensions.Configuration.Binder": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "Microsoft.Net.Http.Headers": "2.2.0", - "System.Memory": "4.5.1", - "System.Numerics.Vectors": "4.5.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.1", - "System.Security.Cryptography.Cng": "4.5.0", - "System.Threading.Tasks.Extensions": "4.5.1" - }, - "compile": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Core.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Core.dll": {} - } - }, - "Microsoft.AspNetCore.Server.Kestrel.Https/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Server.Kestrel.Core": "2.2.0" - }, - "compile": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Https.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Https.dll": {} - } - }, - "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Connections.Abstractions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.dll": {} - } - }, - "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.dll": {} - } - }, - "Microsoft.AspNetCore.Session/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.DataProtection": "2.1.0", - "Microsoft.AspNetCore.Http.Abstractions": "2.1.0", - "Microsoft.Extensions.Caching.Abstractions": "2.1.0", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0", - "Microsoft.Extensions.Options": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.Session.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.Session.dll": {} - } - }, - "Microsoft.AspNetCore.SignalR/1.0.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Connections": "1.0.0", - "Microsoft.AspNetCore.SignalR.Core": "1.0.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.dll": {} - } - }, - "Microsoft.AspNetCore.SignalR.Common/1.0.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Connections.Abstractions": "2.1.0", - "Microsoft.Extensions.Options": "2.1.0", - "Newtonsoft.Json": "11.0.2", - "System.Buffers": "4.5.0" - }, - "compile": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.SignalR.Common.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/Microsoft.AspNetCore.SignalR.Common.dll": {} - } - }, - "Microsoft.AspNetCore.SignalR.Core/1.0.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Authorization": "2.1.0", - "Microsoft.AspNetCore.SignalR.Common": "1.0.0", - "Microsoft.AspNetCore.SignalR.Protocols.Json": "1.0.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0", - "Microsoft.Extensions.Logging.Abstractions": "2.1.0", - "System.Reflection.Emit": "4.3.0", - "System.Threading.Channels": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Core.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Core.dll": {} - } - }, - "Microsoft.AspNetCore.SignalR.Protocols.Json/1.0.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.SignalR.Common": "1.0.0", - "Newtonsoft.Json": "11.0.2" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Protocols.Json.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Protocols.Json.dll": {} - } - }, - "Microsoft.AspNetCore.SpaServices/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Mvc.TagHelpers": "2.1.0", - "Microsoft.AspNetCore.Mvc.ViewFeatures": "2.1.0", - "Microsoft.AspNetCore.NodeServices": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.SpaServices.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.SpaServices.dll": {} - } - }, - "Microsoft.AspNetCore.SpaServices.Extensions/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.SpaServices": "2.1.0", - "Microsoft.AspNetCore.StaticFiles": "2.1.0", - "Microsoft.AspNetCore.WebSockets": "2.1.0", - "Microsoft.Extensions.FileProviders.Physical": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.SpaServices.Extensions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.SpaServices.Extensions.dll": {} - } - }, - "Microsoft.AspNetCore.StaticFiles/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.2.0", - "Microsoft.AspNetCore.Http.Extensions": "2.2.0", - "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.WebEncoders": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.StaticFiles.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.StaticFiles.dll": {} - } - }, - "Microsoft.AspNetCore.WebSockets/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Http.Extensions": "2.1.0", - "Microsoft.Extensions.Options": "2.1.0", - "System.Net.WebSockets.WebSocketProtocol": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.WebSockets.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.WebSockets.dll": {} - } - }, - "Microsoft.AspNetCore.WebUtilities/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Net.Http.Headers": "2.2.0", - "System.Text.Encodings.Web": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll": {} - } - }, - "Microsoft.CodeAnalysis.Analyzers/1.1.0": { - "type": "package" - }, - "Microsoft.CodeAnalysis.Common/2.8.0": { - "type": "package", - "dependencies": { - "Microsoft.CodeAnalysis.Analyzers": "1.1.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Collections.Immutable": "1.3.1", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.FileVersionInfo": "4.3.0", - "System.Diagnostics.StackTrace": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Dynamic.Runtime": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Metadata": "1.4.2", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.CodePages": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Parallel": "4.3.0", - "System.Threading.Thread": "4.3.0", - "System.ValueTuple": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0", - "System.Xml.XPath.XDocument": "4.3.0", - "System.Xml.XmlDocument": "4.3.0" - }, - "compile": { - "lib/netstandard1.3/Microsoft.CodeAnalysis.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Microsoft.CodeAnalysis.dll": {} - } - }, - "Microsoft.CodeAnalysis.CSharp/2.8.0": { - "type": "package", - "dependencies": { - "Microsoft.CodeAnalysis.Common": "[2.8.0]" - }, - "compile": { - "lib/netstandard1.3/Microsoft.CodeAnalysis.CSharp.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Microsoft.CodeAnalysis.CSharp.dll": {} - } - }, - "Microsoft.CodeAnalysis.Razor/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Razor.Language": "2.2.0", - "Microsoft.CodeAnalysis.CSharp": "2.8.0", - "Microsoft.CodeAnalysis.Common": "2.8.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.CodeAnalysis.Razor.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.CodeAnalysis.Razor.dll": {} - } - }, - "Microsoft.CSharp/4.5.0": { - "type": "package", - "compile": { - "ref/netcoreapp2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/_._": {} - } - }, - "Microsoft.DotNet.PlatformAbstractions/2.1.0": { - "type": "package", - "dependencies": { - "System.AppContext": "4.1.0", - "System.Collections": "4.0.11", - "System.IO": "4.1.0", - "System.IO.FileSystem": "4.0.1", - "System.Reflection.TypeExtensions": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Runtime.InteropServices": "4.1.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.0.0" - }, - "compile": { - "lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll": {} - } - }, - "Microsoft.EntityFrameworkCore/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.EntityFrameworkCore.Abstractions": "2.1.0", - "Microsoft.EntityFrameworkCore.Analyzers": "2.1.0", - "Microsoft.Extensions.Caching.Memory": "2.1.0", - "Microsoft.Extensions.DependencyInjection": "2.1.0", - "Microsoft.Extensions.Logging": "2.1.0", - "Remotion.Linq": "2.2.0", - "System.Collections.Immutable": "1.5.0", - "System.ComponentModel.Annotations": "4.5.0", - "System.Diagnostics.DiagnosticSource": "4.5.0", - "System.Interactive.Async": "3.1.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.dll": {} - } - }, - "Microsoft.EntityFrameworkCore.Abstractions/2.1.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Abstractions.dll": {} - } - }, - "Microsoft.EntityFrameworkCore.Analyzers/2.1.0": { - "type": "package" - }, - "Microsoft.EntityFrameworkCore.Design/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.CSharp": "4.5.0", - "Microsoft.EntityFrameworkCore.Relational": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Design.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Design.dll": {} - }, - "build": { - "build/netcoreapp2.0/Microsoft.EntityFrameworkCore.Design.props": {} - } - }, - "Microsoft.EntityFrameworkCore.InMemory/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.EntityFrameworkCore": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.InMemory.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.InMemory.dll": {} - } - }, - "Microsoft.EntityFrameworkCore.Relational/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.EntityFrameworkCore": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Relational.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Relational.dll": {} - } - }, - "Microsoft.EntityFrameworkCore.SqlServer/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.EntityFrameworkCore.Relational": "2.1.0", - "System.Data.SqlClient": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.SqlServer.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.SqlServer.dll": {} - } - }, - "Microsoft.EntityFrameworkCore.Tools/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.EntityFrameworkCore.Design": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/_._": {} - }, - "runtime": { - "lib/netstandard2.0/_._": {} - } - }, - "Microsoft.Extensions.Caching.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.Caching.Memory/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "2.2.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll": {} - } - }, - "Microsoft.Extensions.Caching.SqlServer/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Caching.Abstractions": "2.1.0", - "Microsoft.Extensions.Options": "2.1.0", - "System.Data.SqlClient": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.SqlServer.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Caching.SqlServer.dll": {} - } - }, - "Microsoft.Extensions.Configuration/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll": {} - } - }, - "Microsoft.Extensions.Configuration.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.Configuration.Binder/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll": {} - } - }, - "Microsoft.Extensions.Configuration.CommandLine/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.CommandLine.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.CommandLine.dll": {} - } - }, - "Microsoft.Extensions.Configuration.EnvironmentVariables/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll": {} - } - }, - "Microsoft.Extensions.Configuration.FileExtensions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.2.0", - "Microsoft.Extensions.FileProviders.Physical": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll": {} - } - }, - "Microsoft.Extensions.Configuration.Ini/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0", - "Microsoft.Extensions.Configuration.FileExtensions": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Ini.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Ini.dll": {} - } - }, - "Microsoft.Extensions.Configuration.Json/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.2.0", - "Microsoft.Extensions.Configuration.FileExtensions": "2.2.0", - "Newtonsoft.Json": "11.0.2" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll": {} - } - }, - "Microsoft.Extensions.Configuration.KeyPerFile/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0", - "Microsoft.Extensions.FileProviders.Physical": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.KeyPerFile.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.KeyPerFile.dll": {} - } - }, - "Microsoft.Extensions.Configuration.UserSecrets/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Json": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.dll": {} - }, - "build": { - "build/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.props": {}, - "build/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.targets": {} - } - }, - "Microsoft.Extensions.Configuration.Xml/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0", - "Microsoft.Extensions.Configuration.FileExtensions": "2.1.0", - "System.Security.Cryptography.Xml": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Xml.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Xml.dll": {} - } - }, - "Microsoft.Extensions.DependencyInjection/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0" - }, - "compile": { - "lib/netcoreapp2.0/Microsoft.Extensions.DependencyInjection.dll": {} - }, - "runtime": { - "lib/netcoreapp2.0/Microsoft.Extensions.DependencyInjection.dll": {} - } - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.DependencyModel/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.DotNet.PlatformAbstractions": "2.1.0", - "Newtonsoft.Json": "9.0.1", - "System.Diagnostics.Debug": "4.0.11", - "System.Dynamic.Runtime": "4.0.11", - "System.Linq": "4.1.0" - }, - "compile": { - "lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll": {} - }, - "runtime": { - "lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll": {} - } - }, - "Microsoft.Extensions.DiagnosticAdapter/2.1.0": { - "type": "package", - "dependencies": { - "System.Diagnostics.DiagnosticSource": "4.5.0" - }, - "compile": { - "lib/netcoreapp2.0/Microsoft.Extensions.DiagnosticAdapter.dll": {} - }, - "runtime": { - "lib/netcoreapp2.0/Microsoft.Extensions.DiagnosticAdapter.dll": {} - } - }, - "Microsoft.Extensions.FileProviders.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.FileProviders.Composite/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Composite.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Composite.dll": {} - } - }, - "Microsoft.Extensions.FileProviders.Embedded/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.dll": {} - }, - "build": { - "build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props": {}, - "build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.targets": {} - }, - "buildMultiTargeting": { - "buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.props": {}, - "buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.targets": {} - } - }, - "Microsoft.Extensions.FileProviders.Physical/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", - "Microsoft.Extensions.FileSystemGlobbing": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll": {} - } - }, - "Microsoft.Extensions.FileSystemGlobbing/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll": {} - } - }, - "Microsoft.Extensions.Hosting/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration": "2.1.0", - "Microsoft.Extensions.DependencyInjection": "2.1.0", - "Microsoft.Extensions.FileProviders.Physical": "2.1.0", - "Microsoft.Extensions.Hosting.Abstractions": "2.1.0", - "Microsoft.Extensions.Logging": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Hosting.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Hosting.dll": {} - } - }, - "Microsoft.Extensions.Hosting.Abstractions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "2.2.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.FileProviders.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.Http/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.1.0", - "Microsoft.Extensions.Logging": "2.1.0", - "Microsoft.Extensions.Options": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Http.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Http.dll": {} - } - }, - "Microsoft.Extensions.Identity.Core/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Cryptography.KeyDerivation": "2.1.0", - "Microsoft.Extensions.Logging": "2.1.0", - "Microsoft.Extensions.Options": "2.1.0", - "System.ComponentModel.Annotations": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Identity.Core.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Identity.Core.dll": {} - } - }, - "Microsoft.Extensions.Identity.Stores/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Identity.Core": "2.1.0", - "Microsoft.Extensions.Logging": "2.1.0", - "System.ComponentModel.Annotations": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Identity.Stores.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Identity.Stores.dll": {} - } - }, - "Microsoft.Extensions.Localization/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.Localization.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Localization.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Localization.dll": {} - } - }, - "Microsoft.Extensions.Localization.Abstractions/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Localization.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Localization.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.Logging/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Binder": "2.2.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.dll": {} - } - }, - "Microsoft.Extensions.Logging.Abstractions/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll": {} - } - }, - "Microsoft.Extensions.Logging.Configuration/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Logging": "2.2.0", - "Microsoft.Extensions.Options.ConfigurationExtensions": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Configuration.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Configuration.dll": {} - } - }, - "Microsoft.Extensions.Logging.Console/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "2.2.0", - "Microsoft.Extensions.Logging": "2.2.0", - "Microsoft.Extensions.Logging.Configuration": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Console.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Console.dll": {} - } - }, - "Microsoft.Extensions.Logging.Debug/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Logging": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Debug.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.Debug.dll": {} - } - }, - "Microsoft.Extensions.Logging.EventSource/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Logging": "2.2.0", - "Newtonsoft.Json": "11.0.2" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.EventSource.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.EventSource.dll": {} - } - }, - "Microsoft.Extensions.Logging.TraceSource/2.1.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Logging": "2.1.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.TraceSource.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Logging.TraceSource.dll": {} - } - }, - "Microsoft.Extensions.ObjectPool/2.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll": {} - } - }, - "Microsoft.Extensions.Options/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.Primitives": "2.2.0", - "System.ComponentModel.Annotations": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Options.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Options.dll": {} - } - }, - "Microsoft.Extensions.Options.ConfigurationExtensions/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Configuration.Abstractions": "2.2.0", - "Microsoft.Extensions.Configuration.Binder": "2.2.0", - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll": {} - } - }, - "Microsoft.Extensions.Primitives/2.2.0": { - "type": "package", - "dependencies": { - "System.Memory": "4.5.1", - "System.Runtime.CompilerServices.Unsafe": "4.5.1" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll": {} - } - }, - "Microsoft.Extensions.WebEncoders/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.DependencyInjection.Abstractions": "2.2.0", - "Microsoft.Extensions.Options": "2.2.0", - "System.Text.Encodings.Web": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Extensions.WebEncoders.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Extensions.WebEncoders.dll": {} - } - }, - "Microsoft.IdentityModel.Logging/5.2.0": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0" - }, - "compile": { - "lib/netstandard1.4/Microsoft.IdentityModel.Logging.dll": {} - }, - "runtime": { - "lib/netstandard1.4/Microsoft.IdentityModel.Logging.dll": {} - } - }, - "Microsoft.IdentityModel.Protocols/5.2.0": { - "type": "package", - "dependencies": { - "Microsoft.IdentityModel.Logging": "5.2.0", - "Microsoft.IdentityModel.Tokens": "5.2.0", - "NETStandard.Library": "1.6.1", - "System.Collections.Specialized": "4.3.0", - "System.Diagnostics.Contracts": "4.3.0", - "System.Net.Http": "4.3.0" - }, - "compile": { - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.dll": {} - }, - "runtime": { - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.dll": {} - } - }, - "Microsoft.IdentityModel.Protocols.OpenIdConnect/5.2.0": { - "type": "package", - "dependencies": { - "Microsoft.IdentityModel.Protocols": "5.2.0", - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1", - "System.Dynamic.Runtime": "4.3.0", - "System.IdentityModel.Tokens.Jwt": "5.2.0" - }, - "compile": { - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll": {} - }, - "runtime": { - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll": {} - } - }, - "Microsoft.IdentityModel.Protocols.WsFederation/5.2.0": { - "type": "package", - "dependencies": { - "Microsoft.IdentityModel.Protocols": "5.2.0", - "Microsoft.IdentityModel.Tokens.Saml": "5.2.0", - "Microsoft.IdentityModel.Xml": "5.2.0", - "NETStandard.Library": "1.6.1", - "System.Xml.XmlDocument": "4.3.0" - }, - "compile": { - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.WsFederation.dll": {} - }, - "runtime": { - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.WsFederation.dll": {} - } - }, - "Microsoft.IdentityModel.Tokens/5.2.0": { - "type": "package", - "dependencies": { - "Microsoft.IdentityModel.Logging": "5.2.0", - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1", - "System.Collections": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Serialization.Xml": "4.3.0", - "System.Security.Claims": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "lib/netstandard1.4/Microsoft.IdentityModel.Tokens.dll": {} - }, - "runtime": { - "lib/netstandard1.4/Microsoft.IdentityModel.Tokens.dll": {} - } - }, - "Microsoft.IdentityModel.Tokens.Saml/5.2.0": { - "type": "package", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "5.2.0", - "Microsoft.IdentityModel.Xml": "5.2.0", - "NETStandard.Library": "1.6.1" - }, - "compile": { - "lib/netstandard1.4/Microsoft.IdentityModel.Tokens.Saml.dll": {} - }, - "runtime": { - "lib/netstandard1.4/Microsoft.IdentityModel.Tokens.Saml.dll": {} - } - }, - "Microsoft.IdentityModel.Xml/5.2.0": { - "type": "package", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "5.2.0", - "NETStandard.Library": "1.6.1" - }, - "compile": { - "lib/netstandard1.4/Microsoft.IdentityModel.Xml.dll": {} - }, - "runtime": { - "lib/netstandard1.4/Microsoft.IdentityModel.Xml.dll": {} - } - }, - "Microsoft.IO.RecyclableMemoryStream/1.2.2": { - "type": "package", - "compile": { - "lib/netstandard1.4/Microsoft.IO.RecyclableMemoryStream.dll": {} - }, - "runtime": { - "lib/netstandard1.4/Microsoft.IO.RecyclableMemoryStream.dll": {} - } - }, - "Microsoft.Net.Http.Headers/2.2.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Primitives": "2.2.0", - "System.Buffers": "4.5.0" - }, - "compile": { - "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll": {} - } - }, - "Microsoft.NETCore.Platforms/2.1.0": { - "type": "package", - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "Microsoft.NETCore.Targets/1.1.0": { - "type": "package", - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "Microsoft.Win32.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/Microsoft.Win32.Primitives.dll": {} - } - }, - "Microsoft.Win32.Registry/4.5.0": { - "type": "package", - "dependencies": { - "System.Security.AccessControl": "4.5.0", - "System.Security.Principal.Windows": "4.5.0" - }, - "compile": { - "ref/netstandard2.0/Microsoft.Win32.Registry.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Microsoft.Win32.Registry.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard2.0/Microsoft.Win32.Registry.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard2.0/Microsoft.Win32.Registry.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "MongoDB.Bson/2.7.0": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.Collections.NonGeneric": "4.0.1", - "System.Diagnostics.Process": "4.1.0", - "System.Dynamic.Runtime": "4.0.11", - "System.Reflection.Emit.Lightweight": "4.0.1" - }, - "compile": { - "lib/netstandard1.5/MongoDB.Bson.dll": {} - }, - "runtime": { - "lib/netstandard1.5/MongoDB.Bson.dll": {} - } - }, - "Multiformats.Base/2.0.1": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1", - "System.Runtime.Numerics": "4.3.0" - }, - "compile": { - "lib/netstandard1.6/Multiformats.Base.dll": {} - }, - "runtime": { - "lib/netstandard1.6/Multiformats.Base.dll": {} - } - }, - "Multiformats.Hash/1.5.0": { - "type": "package", - "dependencies": { - "BinaryEncoding": "1.4.0", - "Multiformats.Base": "2.0.1", - "Portable.BouncyCastle": "1.8.5", - "System.Composition": "1.2.0", - "murmurhash": "1.0.2" - }, - "compile": { - "lib/netstandard2.0/Multiformats.Hash.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Multiformats.Hash.dll": {} - } - }, - "murmurhash/1.0.2": { - "type": "package", - "compile": { - "lib/netstandard1.4/MurmurHash.dll": {} - }, - "runtime": { - "lib/netstandard1.4/MurmurHash.dll": {} - } - }, - "Nethereum.Hex/3.3.0": { - "type": "package", - "dependencies": { - "Newtonsoft.Json": "[10.0.3, 13.0.0)", - "Portable.BouncyCastle": "1.8.2" - }, - "compile": { - "lib/netcoreapp2.1/Nethereum.Hex.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/Nethereum.Hex.dll": {} - } - }, - "Nethereum.RLP/3.3.0": { - "type": "package", - "dependencies": { - "Nethereum.Hex": "3.3.0", - "Newtonsoft.Json": "[10.0.3, 13.0.0)", - "Portable.BouncyCastle": "1.8.2" - }, - "compile": { - "lib/netcoreapp2.1/Nethereum.RLP.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/Nethereum.RLP.dll": {} - } - }, - "NETStandard.Library/1.6.1": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.AppContext": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Console": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.Compression.ZipFile": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Net.Http": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Timer": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0" - } - }, - "Newtonsoft.Json/12.0.2": { - "type": "package", - "compile": { - "lib/netstandard2.0/Newtonsoft.Json.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Newtonsoft.Json.dll": {} - } - }, - "Newtonsoft.Json.Bson/1.0.1": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" - }, - "compile": { - "lib/netstandard1.3/Newtonsoft.Json.Bson.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Newtonsoft.Json.Bson.dll": {} - } - }, - "Nito.AsyncEx/5.0.0": { - "type": "package", - "dependencies": { - "Nito.AsyncEx.Context": "5.0.0", - "Nito.AsyncEx.Coordination": "5.0.0", - "Nito.AsyncEx.Interop.WaitHandles": "5.0.0", - "Nito.AsyncEx.Oop": "5.0.0", - "Nito.AsyncEx.Tasks": "5.0.0", - "Nito.Cancellation": "1.0.5" - } - }, - "Nito.AsyncEx.Context/5.0.0": { - "type": "package", - "dependencies": { - "Nito.AsyncEx.Tasks": "5.0.0" - }, - "compile": { - "lib/netstandard2.0/Nito.AsyncEx.Context.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.AsyncEx.Context.dll": {} - } - }, - "Nito.AsyncEx.Coordination/5.0.0": { - "type": "package", - "dependencies": { - "Nito.AsyncEx.Tasks": "5.0.0", - "Nito.Collections.Deque": "1.0.4", - "Nito.Disposables": "2.0.0" - }, - "compile": { - "lib/netstandard2.0/Nito.AsyncEx.Coordination.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.AsyncEx.Coordination.dll": {} - } - }, - "Nito.AsyncEx.Interop.WaitHandles/5.0.0": { - "type": "package", - "dependencies": { - "Nito.AsyncEx.Tasks": "5.0.0" - }, - "compile": { - "lib/netstandard2.0/Nito.AsyncEx.Interop.WaitHandles.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.AsyncEx.Interop.WaitHandles.dll": {} - } - }, - "Nito.AsyncEx.Oop/5.0.0": { - "type": "package", - "dependencies": { - "Nito.AsyncEx.Coordination": "5.0.0" - }, - "compile": { - "lib/netstandard2.0/Nito.AsyncEx.Oop.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.AsyncEx.Oop.dll": {} - } - }, - "Nito.AsyncEx.Tasks/5.0.0": { - "type": "package", - "dependencies": { - "Nito.Disposables": "2.0.0" - }, - "compile": { - "lib/netstandard2.0/Nito.AsyncEx.Tasks.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.AsyncEx.Tasks.dll": {} - } - }, - "Nito.Cancellation/1.0.5": { - "type": "package", - "dependencies": { - "Nito.Disposables": "1.2.3" - }, - "compile": { - "lib/netstandard2.0/Nito.Cancellation.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.Cancellation.dll": {} - } - }, - "Nito.Collections.Deque/1.0.4": { - "type": "package", - "compile": { - "lib/netstandard2.0/Nito.Collections.Deque.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.Collections.Deque.dll": {} - } - }, - "Nito.Disposables/2.0.0": { - "type": "package", - "dependencies": { - "System.Collections.Immutable": "1.4.0" - }, - "compile": { - "lib/netstandard2.0/Nito.Disposables.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Nito.Disposables.dll": {} - } - }, - "NLog/4.6.5": { - "type": "package", - "compile": { - "lib/netstandard2.0/NLog.dll": {} - }, - "runtime": { - "lib/netstandard2.0/NLog.dll": {} - } - }, - "NLog.StructuredLogging.Json/4.0.0": { - "type": "package", - "dependencies": { - "NLog": "4.5.0", - "Newtonsoft.Json": "9.0.1" - }, - "compile": { - "lib/netstandard2.0/NLog.StructuredLogging.Json.dll": {} - }, - "runtime": { - "lib/netstandard2.0/NLog.StructuredLogging.Json.dll": {} - } - }, - "NLog.Targets.Seq/1.1.0": { - "type": "package", - "dependencies": { - "NLog": "4.5.11" - }, - "compile": { - "lib/netstandard2.0/NLog.Targets.Seq.dll": {} - }, - "runtime": { - "lib/netstandard2.0/NLog.Targets.Seq.dll": {} - } - }, - "NLog.Targets.Syslog/5.1.0": { - "type": "package", - "dependencies": { - "NLog": "4.5.4" - }, - "compile": { - "lib/netstandard2.0/NLog.Targets.Syslog.dll": {} - }, - "runtime": { - "lib/netstandard2.0/NLog.Targets.Syslog.dll": {} - } - }, - "PeerTalk/0.11.3": { - "type": "package", - "dependencies": { - "Ipfs.Core": "0.50.1", - "Makaretu.Dns.Multicast": "0.18.0", - "Makaretu.KBucket": "0.5.0", - "Nito.AsyncEx": "5.0.0", - "Portable.BouncyCastle": "1.8.5", - "System.Threading.Tasks.Dataflow": "4.9.0", - "protobuf-net": "2.4.0", - "semver": "2.0.4" - }, - "compile": { - "lib/netstandard2.0/PeerTalk.dll": {} - }, - "runtime": { - "lib/netstandard2.0/PeerTalk.dll": {} - } - }, - "PeterO.Cbor/3.1.0": { - "type": "package", - "dependencies": { - "PeterO.Numbers": "1.0.2" - }, - "compile": { - "lib/netstandard1.0/CBOR.dll": {} - }, - "runtime": { - "lib/netstandard1.0/CBOR.dll": {} - } - }, - "PeterO.Numbers/1.0.2": { - "type": "package", - "compile": { - "lib/netstandard1.0/Numbers.dll": {} - }, - "runtime": { - "lib/netstandard1.0/Numbers.dll": {} - } - }, - "Polly/7.1.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Polly.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Polly.dll": {} - } - }, - "Portable.BouncyCastle/1.8.5": { - "type": "package", - "compile": { - "lib/netstandard2.0/BouncyCastle.Crypto.dll": {} - }, - "runtime": { - "lib/netstandard2.0/BouncyCastle.Crypto.dll": {} - } - }, - "protobuf-net/2.4.0": { - "type": "package", - "dependencies": { - "System.ServiceModel.Primitives": "4.5.3" - }, - "compile": { - "lib/netcoreapp2.1/protobuf-net.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/protobuf-net.dll": {} - } - }, - "Remotion.Linq/2.2.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.0.11", - "System.Diagnostics.Debug": "4.0.11", - "System.Linq": "4.1.0", - "System.Linq.Expressions": "4.1.0", - "System.Linq.Queryable": "4.0.1", - "System.ObjectModel": "4.0.12", - "System.Reflection": "4.1.0", - "System.Reflection.Extensions": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Threading": "4.0.11" - }, - "compile": { - "lib/netstandard1.0/Remotion.Linq.dll": {} - }, - "runtime": { - "lib/netstandard1.0/Remotion.Linq.dll": {} - } - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/debian.8-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "debian.8-x64" - } - } - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/fedora.23-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "fedora.23-x64" - } - } - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/fedora.24-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "fedora.24-x64" - } - } - }, - "runtime.native.System/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Data.SqlClient.sni/4.4.0": { - "type": "package", - "dependencies": { - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni": "4.4.0", - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni": "4.4.0" - } - }, - "runtime.native.System.IO.Compression/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Net.Http/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Net.Security/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Security.Cryptography.Apple/4.3.1": { - "type": "package", - "dependencies": { - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple": "4.3.1" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "dependencies": { - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2", - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - }, - "compile": { - "lib/netstandard1.0/_._": {} - }, - "runtime": { - "lib/netstandard1.0/_._": {} - } - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/opensuse.13.2-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "opensuse.13.2-x64" - } - } - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/opensuse.42.1-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "opensuse.42.1-x64" - } - } - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.1": { - "type": "package", - "runtimeTargets": { - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.Apple.dylib": { - "assetType": "native", - "rid": "osx.10.10-x64" - } - } - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.OpenSsl.dylib": { - "assetType": "native", - "rid": "osx.10.10-x64" - } - } - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/rhel.7-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "rhel.7-x64" - } - } - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/ubuntu.14.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "ubuntu.14.04-x64" - } - } - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/ubuntu.16.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "ubuntu.16.04-x64" - } - } - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "type": "package", - "runtimeTargets": { - "runtimes/ubuntu.16.10-x64/native/System.Security.Cryptography.Native.OpenSsl.so": { - "assetType": "native", - "rid": "ubuntu.16.10-x64" - } - } - }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "type": "package", - "runtimeTargets": { - "runtimes/win-arm64/native/sni.dll": { - "assetType": "native", - "rid": "win-arm64" - } - } - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "type": "package", - "runtimeTargets": { - "runtimes/win-x64/native/sni.dll": { - "assetType": "native", - "rid": "win-x64" - } - } - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "type": "package", - "runtimeTargets": { - "runtimes/win-x86/native/sni.dll": { - "assetType": "native", - "rid": "win-x86" - } - } - }, - "Semver/2.0.4": { - "type": "package", - "dependencies": { - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Runtime.InteropServices": "4.1.0", - "System.Text.RegularExpressions": "4.1.0" - }, - "compile": { - "lib/netstandard1.1/Semver.dll": {} - }, - "runtime": { - "lib/netstandard1.1/Semver.dll": {} - } - }, - "Serilog/2.6.0": { - "type": "package", - "dependencies": { - "Microsoft.CSharp": "4.0.1", - "System.Collections": "4.0.11", - "System.Collections.NonGeneric": "4.0.1", - "System.Dynamic.Runtime": "4.0.11", - "System.Globalization": "4.0.11", - "System.Linq": "4.1.0", - "System.Reflection": "4.1.0", - "System.Reflection.Extensions": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Text.RegularExpressions": "4.1.0", - "System.Threading": "4.0.11" - }, - "compile": { - "lib/netstandard1.3/Serilog.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Serilog.dll": {} - } - }, - "Serilog.AspNetCore/2.1.1": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Hosting.Abstractions": "2.0.0", - "Microsoft.Extensions.DependencyInjection": "2.0.0", - "Microsoft.Extensions.Logging": "2.0.0", - "Serilog": "2.5.0", - "Serilog.Extensions.Logging": "2.0.0" - }, - "compile": { - "lib/netstandard2.0/Serilog.AspNetCore.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Serilog.AspNetCore.dll": {} - } - }, - "Serilog.Enrichers.Environment/2.1.3": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.1", - "Serilog": "2.3.0" - }, - "compile": { - "lib/netstandard1.5/Serilog.Enrichers.Environment.dll": {} - }, - "runtime": { - "lib/netstandard1.5/Serilog.Enrichers.Environment.dll": {} - } - }, - "Serilog.Enrichers.Thread/3.1.0": { - "type": "package", - "dependencies": { - "Serilog": "2.3.0" - }, - "compile": { - "lib/netstandard2.0/Serilog.Enrichers.Thread.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Serilog.Enrichers.Thread.dll": {} - } - }, - "Serilog.Extensions.Logging/2.0.4": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Logging": "2.0.0", - "Serilog": "2.3.0" - }, - "compile": { - "lib/netstandard2.0/Serilog.Extensions.Logging.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Serilog.Extensions.Logging.dll": {} - } - }, - "Serilog.Settings.Configuration/3.1.0": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.DependencyModel": "2.0.4", - "Microsoft.Extensions.Options.ConfigurationExtensions": "2.0.0", - "Serilog": "2.6.0" - }, - "compile": { - "lib/netstandard2.0/Serilog.Settings.Configuration.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Serilog.Settings.Configuration.dll": {} - } - }, - "Serilog.Sinks.Console/3.1.1": { - "type": "package", - "dependencies": { - "Serilog": "2.5.0", - "System.Console": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.InteropServices.RuntimeInformation": "4.3.0" - }, - "compile": { - "lib/netcoreapp1.1/Serilog.Sinks.Console.dll": {} - }, - "runtime": { - "lib/netcoreapp1.1/Serilog.Sinks.Console.dll": {} - } - }, - "Serilog.Sinks.File/4.0.0": { - "type": "package", - "dependencies": { - "Serilog": "2.5.0", - "System.IO": "4.1.0", - "System.IO.FileSystem": "4.0.1", - "System.IO.FileSystem.Primitives": "4.0.1", - "System.Runtime.InteropServices": "4.1.0", - "System.Text.Encoding.Extensions": "4.0.11", - "System.Threading": "4.0.11", - "System.Threading.Timer": "4.0.1" - }, - "compile": { - "lib/netstandard1.3/Serilog.Sinks.File.dll": {} - }, - "runtime": { - "lib/netstandard1.3/Serilog.Sinks.File.dll": {} - } - }, - "SharpRepository.Ioc.Autofac/2.0.4.2": { - "type": "package", - "dependencies": { - "Autofac": "4.8.1", - "NETStandard.Library": "1.6.1", - "SharpRepository.Repository": "2.0.4.6" - }, - "compile": { - "lib/netstandard1.3/SharpRepository.Ioc.Autofac.dll": {} - }, - "runtime": { - "lib/netstandard1.3/SharpRepository.Ioc.Autofac.dll": {} - } - }, - "SharpRepository.Repository/2.0.4.6": { - "type": "package", - "dependencies": { - "Microsoft.Extensions.Caching.Memory": "2.1.2", - "Microsoft.Extensions.Configuration.Json": "2.1.1", - "System.Linq.Queryable": "4.3.0", - "System.Reflection.TypeExtensions": "4.5.1", - "System.Security.Cryptography.Algorithms": "4.3.1" - }, - "compile": { - "lib/netstandard2.0/SharpRepository.Repository.dll": {} - }, - "runtime": { - "lib/netstandard2.0/SharpRepository.Repository.dll": {} - }, - "contentFiles": { - "contentFiles/any/any/_._": { - "buildAction": "None", - "codeLanguage": "any", - "copyToOutput": false - } - } - }, - "SharpRepository.XmlRepository/2.0.1-alpha3": { - "type": "package", - "dependencies": { - "SharpRepository.Repository": "2.0.1-beta1" - }, - "compile": { - "lib/netstandard2.0/SharpRepository.XmlRepository.dll": {} - }, - "runtime": { - "lib/netstandard2.0/SharpRepository.XmlRepository.dll": {} - }, - "contentFiles": { - "contentFiles/any/any/_._": { - "buildAction": "None", - "codeLanguage": "any", - "copyToOutput": false - } - } - }, - "SharpZipLib/1.0.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/ICSharpCode.SharpZipLib.dll": {} - }, - "runtime": { - "lib/netstandard2.0/ICSharpCode.SharpZipLib.dll": {} - } - }, - "SimpleBase/1.3.1": { - "type": "package", - "compile": { - "lib/netstandard1.3/SimpleBase.dll": {} - }, - "runtime": { - "lib/netstandard1.3/SimpleBase.dll": {} - } - }, - "Swashbuckle.AspNetCore.Swagger/4.0.1": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Mvc.Core": "2.0.0", - "Microsoft.AspNetCore.Mvc.Formatters.Json": "2.0.0" - }, - "compile": { - "lib/netstandard2.0/Swashbuckle.AspNetCore.Swagger.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Swashbuckle.AspNetCore.Swagger.dll": {} - } - }, - "Swashbuckle.AspNetCore.SwaggerGen/4.0.1": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Mvc.ApiExplorer": "2.0.0", - "Microsoft.AspNetCore.Mvc.Core": "2.0.0", - "Microsoft.AspNetCore.Mvc.DataAnnotations": "2.0.0", - "Swashbuckle.AspNetCore.Swagger": "4.0.1" - }, - "compile": { - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerGen.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerGen.dll": {} - } - }, - "Swashbuckle.AspNetCore.SwaggerUI/4.0.1": { - "type": "package", - "dependencies": { - "Microsoft.AspNetCore.Routing": "2.0.0", - "Microsoft.AspNetCore.StaticFiles": "2.0.0", - "Microsoft.Extensions.FileProviders.Embedded": "2.0.0", - "Newtonsoft.Json": "9.0.1" - }, - "compile": { - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerUI.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerUI.dll": {} - } - }, - "System.AppContext/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/System.AppContext.dll": {} - }, - "runtime": { - "lib/netstandard1.6/System.AppContext.dll": {} - } - }, - "System.Buffers/4.5.0": { - "type": "package", - "compile": { - "ref/netcoreapp2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/_._": {} - } - }, - "System.Collections/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Collections.dll": {} - } - }, - "System.Collections.Concurrent/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Collections.Concurrent.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Collections.Concurrent.dll": {} - } - }, - "System.Collections.Immutable/1.5.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Collections.Immutable.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Collections.Immutable.dll": {} - } - }, - "System.Collections.NonGeneric/4.3.0": { - "type": "package", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Collections.NonGeneric.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Collections.NonGeneric.dll": {} - } - }, - "System.Collections.Specialized/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections.NonGeneric": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Collections.Specialized.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Collections.Specialized.dll": {} - } - }, - "System.ComponentModel/4.0.1": { - "type": "package", - "dependencies": { - "System.Runtime": "4.1.0" - }, - "compile": { - "ref/netstandard1.0/System.ComponentModel.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.ComponentModel.dll": {} - } - }, - "System.ComponentModel.Annotations/4.5.0": { - "type": "package", - "compile": { - "ref/netcoreapp2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/_._": {} - } - }, - "System.Composition/1.2.0": { - "type": "package", - "dependencies": { - "System.Composition.AttributedModel": "1.2.0", - "System.Composition.Convention": "1.2.0", - "System.Composition.Hosting": "1.2.0", - "System.Composition.Runtime": "1.2.0", - "System.Composition.TypedParts": "1.2.0" - } - }, - "System.Composition.AttributedModel/1.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Composition.AttributedModel.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Composition.AttributedModel.dll": {} - } - }, - "System.Composition.Convention/1.2.0": { - "type": "package", - "dependencies": { - "System.Composition.AttributedModel": "1.2.0" - }, - "compile": { - "lib/netstandard2.0/System.Composition.Convention.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Composition.Convention.dll": {} - } - }, - "System.Composition.Hosting/1.2.0": { - "type": "package", - "dependencies": { - "System.Composition.Runtime": "1.2.0" - }, - "compile": { - "lib/netstandard2.0/System.Composition.Hosting.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Composition.Hosting.dll": {} - } - }, - "System.Composition.Runtime/1.2.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Composition.Runtime.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Composition.Runtime.dll": {} - } - }, - "System.Composition.TypedParts/1.2.0": { - "type": "package", - "dependencies": { - "System.Composition.AttributedModel": "1.2.0", - "System.Composition.Hosting": "1.2.0", - "System.Composition.Runtime": "1.2.0" - }, - "compile": { - "lib/netstandard2.0/System.Composition.TypedParts.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Composition.TypedParts.dll": {} - } - }, - "System.Console/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Console.dll": {} - } - }, - "System.Data.SqlClient/4.5.0": { - "type": "package", - "dependencies": { - "Microsoft.Win32.Registry": "4.5.0", - "System.Security.Principal.Windows": "4.5.0", - "System.Text.Encoding.CodePages": "4.5.0", - "runtime.native.System.Data.SqlClient.sni": "4.4.0" - }, - "compile": { - "ref/netcoreapp2.1/System.Data.SqlClient.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/System.Data.SqlClient.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Diagnostics.Contracts/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Diagnostics.Contracts.dll": {} - }, - "runtime": { - "lib/netstandard1.0/System.Diagnostics.Contracts.dll": {} - } - }, - "System.Diagnostics.Debug/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Diagnostics.Debug.dll": {} - } - }, - "System.Diagnostics.DiagnosticSource/4.5.0": { - "type": "package", - "compile": { - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll": {} - } - }, - "System.Diagnostics.FileVersionInfo/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Reflection.Metadata": "1.4.1", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Diagnostics.FileVersionInfo.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Diagnostics.FileVersionInfo.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Diagnostics.Process/4.1.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.0.1", - "Microsoft.Win32.Primitives": "4.0.1", - "Microsoft.Win32.Registry": "4.0.0", - "System.Collections": "4.0.11", - "System.Diagnostics.Debug": "4.0.11", - "System.Globalization": "4.0.11", - "System.IO": "4.1.0", - "System.IO.FileSystem": "4.0.1", - "System.IO.FileSystem.Primitives": "4.0.1", - "System.Resources.ResourceManager": "4.0.1", - "System.Runtime": "4.1.0", - "System.Runtime.Extensions": "4.1.0", - "System.Runtime.Handles": "4.0.1", - "System.Runtime.InteropServices": "4.1.0", - "System.Text.Encoding": "4.0.11", - "System.Text.Encoding.Extensions": "4.0.11", - "System.Threading": "4.0.11", - "System.Threading.Tasks": "4.0.11", - "System.Threading.Thread": "4.0.0", - "System.Threading.ThreadPool": "4.0.10", - "runtime.native.System": "4.0.0" - }, - "compile": { - "ref/netstandard1.4/System.Diagnostics.Process.dll": {} - }, - "runtimeTargets": { - "runtimes/linux/lib/netstandard1.4/System.Diagnostics.Process.dll": { - "assetType": "runtime", - "rid": "linux" - }, - "runtimes/osx/lib/netstandard1.4/System.Diagnostics.Process.dll": { - "assetType": "runtime", - "rid": "osx" - }, - "runtimes/win/lib/netstandard1.4/System.Diagnostics.Process.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Diagnostics.StackTrace/4.3.0": { - "type": "package", - "dependencies": { - "System.IO.FileSystem": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Metadata": "1.4.1", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Diagnostics.StackTrace.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Diagnostics.StackTrace.dll": {} - } - }, - "System.Diagnostics.Tools/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Diagnostics.Tools.dll": {} - } - }, - "System.Diagnostics.Tracing/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.Diagnostics.Tracing.dll": {} - } - }, - "System.Dynamic.Runtime/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Dynamic.Runtime.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Dynamic.Runtime.dll": {} - } - }, - "System.Globalization/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Globalization.dll": {} - } - }, - "System.Globalization.Calendars/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Globalization.Calendars.dll": {} - } - }, - "System.Globalization.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Globalization.Extensions.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Globalization.Extensions.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Globalization.Extensions.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.IdentityModel.Tokens.Jwt/5.2.0": { - "type": "package", - "dependencies": { - "Microsoft.IdentityModel.Tokens": "5.2.0", - "NETStandard.Library": "1.6.1", - "Newtonsoft.Json": "10.0.1" - }, - "compile": { - "lib/netstandard1.4/System.IdentityModel.Tokens.Jwt.dll": {} - }, - "runtime": { - "lib/netstandard1.4/System.IdentityModel.Tokens.Jwt.dll": {} - } - }, - "System.Interactive.Async/3.1.1": { - "type": "package", - "dependencies": { - "NETStandard.Library": "1.6.0" - }, - "compile": { - "lib/netstandard1.3/System.Interactive.Async.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Interactive.Async.dll": {} - } - }, - "System.IO/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.IO.dll": {} - } - }, - "System.IO.Abstractions/6.0.3": { - "type": "package", - "dependencies": { - "System.IO.FileSystem.AccessControl": "4.5.0" - }, - "compile": { - "lib/netcoreapp2.0/System.IO.Abstractions.dll": {} - }, - "runtime": { - "lib/netcoreapp2.0/System.IO.Abstractions.dll": {} - } - }, - "System.IO.Compression/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Buffers": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.IO.Compression": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.Compression.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.IO.Compression.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.IO.Compression.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.IO.Compression.ZipFile/4.3.0": { - "type": "package", - "dependencies": { - "System.Buffers": "4.3.0", - "System.IO": "4.3.0", - "System.IO.Compression": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.Compression.ZipFile.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.IO.Compression.ZipFile.dll": {} - } - }, - "System.IO.FileSystem/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.FileSystem.dll": {} - } - }, - "System.IO.FileSystem.AccessControl/4.5.0": { - "type": "package", - "dependencies": { - "System.Security.AccessControl": "4.5.0", - "System.Security.Principal.Windows": "4.5.0" - }, - "compile": { - "ref/netstandard2.0/System.IO.FileSystem.AccessControl.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.IO.FileSystem.AccessControl.dll": {} - }, - "runtimeTargets": { - "runtimes/win/lib/netstandard2.0/System.IO.FileSystem.AccessControl.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.IO.FileSystem.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.IO.FileSystem.Primitives.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.IO.FileSystem.Primitives.dll": {} - } - }, - "System.IO.Pipelines/4.5.2": { - "type": "package", - "compile": { - "ref/netstandard1.3/System.IO.Pipelines.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/System.IO.Pipelines.dll": {} - } - }, - "System.Linq/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/System.Linq.dll": {} - }, - "runtime": { - "lib/netstandard1.6/System.Linq.dll": {} - } - }, - "System.Linq.Expressions/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.ObjectModel": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/System.Linq.Expressions.dll": {} - }, - "runtime": { - "lib/netstandard1.6/System.Linq.Expressions.dll": {} - } - }, - "System.Linq.Queryable/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Linq": "4.3.0", - "System.Linq.Expressions": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Linq.Queryable.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Linq.Queryable.dll": {} - } - }, - "System.Memory/4.5.2": { - "type": "package", - "compile": { - "ref/netcoreapp2.1/_._": {} - }, - "runtime": { - "lib/netcoreapp2.1/_._": {} - } - }, - "System.Net.Http/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.DiagnosticSource": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.Http.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Net.Http.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Net.Http.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Net.NameResolution/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Principal.Windows": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "runtime.native.System": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.NameResolution.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Net.NameResolution.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Net.NameResolution.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Net.NetworkInformation/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Linq": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Net.Sockets": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Principal.Windows": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Overlapped": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Thread": "4.3.0", - "System.Threading.ThreadPool": "4.3.0", - "runtime.native.System": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.NetworkInformation.dll": {} - }, - "runtimeTargets": { - "runtimes/linux/lib/netstandard1.3/System.Net.NetworkInformation.dll": { - "assetType": "runtime", - "rid": "linux" - }, - "runtimes/osx/lib/netstandard1.3/System.Net.NetworkInformation.dll": { - "assetType": "runtime", - "rid": "osx" - }, - "runtimes/win/lib/netstandard1.3/System.Net.NetworkInformation.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Net.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.Primitives.dll": {} - } - }, - "System.Net.Security/4.3.2": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.Win32.Primitives": "4.3.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Extensions": "4.3.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Claims": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Security.Cryptography.X509Certificates": "4.3.0", - "System.Security.Principal": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.ThreadPool": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Security": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - }, - "compile": { - "ref/netstandard1.3/System.Net.Security.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Net.Security.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Net.Security.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Net.Sockets/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Net.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Net.Sockets.dll": {} - } - }, - "System.Net.WebSockets.WebSocketProtocol/4.5.0": { - "type": "package", - "compile": { - "ref/netstandard2.0/System.Net.WebSockets.WebSocketProtocol.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/System.Net.WebSockets.WebSocketProtocol.dll": {} - } - }, - "System.Numerics.Vectors/4.5.0": { - "type": "package", - "compile": { - "ref/netcoreapp2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/_._": {} - } - }, - "System.ObjectModel/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.ObjectModel.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.ObjectModel.dll": {} - } - }, - "System.Private.DataContractSerialization/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Emit.Lightweight": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0", - "System.Xml.XmlDocument": "4.3.0", - "System.Xml.XmlSerializer": "4.3.0" - }, - "compile": { - "ref/netstandard/_._": {} - }, - "runtime": { - "lib/netstandard1.3/System.Private.DataContractSerialization.dll": {} - } - }, - "System.Private.ServiceModel/4.5.3": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "2.1.0", - "System.Reflection.DispatchProxy": "4.5.0", - "System.Security.Principal.Windows": "4.5.0" - }, - "compile": { - "ref/netstandard/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard2.0/System.Private.ServiceModel.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard2.0/System.Private.ServiceModel.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Reactive/4.1.6": { - "type": "package", - "dependencies": { - "System.Runtime.InteropServices.WindowsRuntime": "4.3.0", - "System.Threading.Tasks.Extensions": "4.5.2" - }, - "compile": { - "lib/netstandard2.0/System.Reactive.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Reactive.dll": {} - } - }, - "System.Reflection/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.Reflection.dll": {} - } - }, - "System.Reflection.DispatchProxy/4.5.0": { - "type": "package", - "compile": { - "ref/netstandard2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/System.Reflection.DispatchProxy.dll": {} - } - }, - "System.Reflection.Emit/4.3.0": { - "type": "package", - "dependencies": { - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.1/System.Reflection.Emit.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Reflection.Emit.dll": {} - } - }, - "System.Reflection.Emit.ILGeneration/4.3.0": { - "type": "package", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Reflection.Emit.ILGeneration.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Reflection.Emit.ILGeneration.dll": {} - } - }, - "System.Reflection.Emit.Lightweight/4.3.0": { - "type": "package", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Reflection.Emit.Lightweight.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Reflection.Emit.Lightweight.dll": {} - } - }, - "System.Reflection.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Reflection.Extensions.dll": {} - } - }, - "System.Reflection.Metadata/1.6.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Reflection.Metadata.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Reflection.Metadata.dll": {} - } - }, - "System.Reflection.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Reflection.Primitives.dll": {} - } - }, - "System.Reflection.TypeExtensions/4.5.1": { - "type": "package", - "compile": { - "ref/netcoreapp2.0/_._": {} - }, - "runtime": { - "lib/netcoreapp2.0/_._": {} - } - }, - "System.Resources.ResourceManager/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Globalization": "4.3.0", - "System.Reflection": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Resources.ResourceManager.dll": {} - } - }, - "System.Runtime/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0" - }, - "compile": { - "ref/netstandard1.5/System.Runtime.dll": {} - } - }, - "System.Runtime.CompilerServices.Unsafe/4.5.2": { - "type": "package", - "compile": { - "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll": {} - }, - "runtime": { - "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll": {} - } - }, - "System.Runtime.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.5/System.Runtime.Extensions.dll": {} - } - }, - "System.Runtime.Handles/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Runtime.Handles.dll": {} - } - }, - "System.Runtime.InteropServices/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Reflection": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compile": { - "ref/netcoreapp1.1/System.Runtime.InteropServices.dll": {} - } - }, - "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { - "type": "package", - "dependencies": { - "System.Reflection": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0" - }, - "compile": { - "ref/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": {} - }, - "runtime": { - "lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Runtime.InteropServices.WindowsRuntime/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Runtime.InteropServices.WindowsRuntime.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Runtime.InteropServices.WindowsRuntime.dll": {} - } - }, - "System.Runtime.Numerics/4.3.0": { - "type": "package", - "dependencies": { - "System.Globalization": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0" - }, - "compile": { - "ref/netstandard1.1/System.Runtime.Numerics.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Runtime.Numerics.dll": {} - } - }, - "System.Runtime.Serialization.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Runtime.Serialization.Primitives.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Runtime.Serialization.Primitives.dll": {} - } - }, - "System.Runtime.Serialization.Xml/4.3.0": { - "type": "package", - "dependencies": { - "System.IO": "4.3.0", - "System.Private.DataContractSerialization": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Serialization.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Runtime.Serialization.Xml.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Runtime.Serialization.Xml.dll": {} - } - }, - "System.Security.AccessControl/4.5.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "2.0.0", - "System.Security.Principal.Windows": "4.5.0" - }, - "compile": { - "ref/netstandard2.0/System.Security.AccessControl.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Security.AccessControl.dll": {} - }, - "runtimeTargets": { - "runtimes/win/lib/netcoreapp2.0/System.Security.AccessControl.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Claims/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Security.Principal": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Security.Claims.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Security.Claims.dll": {} - } - }, - "System.Security.Cryptography.Algorithms/4.3.1": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.Apple": "4.3.1", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.2" - }, - "compile": { - "ref/netstandard1.6/System.Security.Cryptography.Algorithms.dll": {} - }, - "runtimeTargets": { - "runtimes/osx/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { - "assetType": "runtime", - "rid": "osx" - }, - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.Cng/4.5.0": { - "type": "package", - "compile": { - "ref/netcoreapp2.1/System.Security.Cryptography.Cng.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/System.Security.Cryptography.Cng.dll": {} - }, - "runtimeTargets": { - "runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Cng.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.Csp/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Csp.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Csp.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.Encoding/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Collections.Concurrent": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Security.Cryptography.Encoding.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.OpenSsl/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.6/_._": {} - }, - "runtime": { - "lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll": { - "assetType": "runtime", - "rid": "unix" - } - } - }, - "System.Security.Cryptography.Pkcs/4.5.0": { - "type": "package", - "dependencies": { - "System.Security.Cryptography.Cng": "4.5.0" - }, - "compile": { - "ref/netcoreapp2.1/_._": {} - }, - "runtime": { - "lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll": {} - }, - "runtimeTargets": { - "runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.Primitives/4.3.0": { - "type": "package", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Security.Cryptography.Primitives.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Security.Cryptography.Primitives.dll": {} - } - }, - "System.Security.Cryptography.X509Certificates/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.Globalization.Calendars": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.Handles": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Runtime.Numerics": "4.3.0", - "System.Security.Cryptography.Algorithms": "4.3.0", - "System.Security.Cryptography.Cng": "4.3.0", - "System.Security.Cryptography.Csp": "4.3.0", - "System.Security.Cryptography.Encoding": "4.3.0", - "System.Security.Cryptography.OpenSsl": "4.3.0", - "System.Security.Cryptography.Primitives": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "runtime.native.System": "4.3.0", - "runtime.native.System.Net.Http": "4.3.0", - "runtime.native.System.Security.Cryptography.OpenSsl": "4.3.0" - }, - "compile": { - "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Security.Cryptography.Xml/4.5.0": { - "type": "package", - "dependencies": { - "System.Security.Cryptography.Pkcs": "4.5.0", - "System.Security.Permissions": "4.5.0" - }, - "compile": { - "ref/netstandard2.0/System.Security.Cryptography.Xml.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Security.Cryptography.Xml.dll": {} - } - }, - "System.Security.Permissions/4.5.0": { - "type": "package", - "dependencies": { - "System.Security.AccessControl": "4.5.0" - }, - "compile": { - "ref/netstandard2.0/System.Security.Permissions.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Security.Permissions.dll": {} - } - }, - "System.Security.Principal/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.0/System.Security.Principal.dll": {} - }, - "runtime": { - "lib/netstandard1.0/System.Security.Principal.dll": {} - } - }, - "System.Security.Principal.Windows/4.5.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "2.0.0" - }, - "compile": { - "ref/netstandard2.0/System.Security.Principal.Windows.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Security.Principal.Windows.dll": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netcoreapp2.0/System.Security.Principal.Windows.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netcoreapp2.0/System.Security.Principal.Windows.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.ServiceModel.Primitives/4.5.3": { - "type": "package", - "dependencies": { - "System.Private.ServiceModel": "4.5.3" - }, - "compile": { - "ref/netstandard2.0/System.ServiceModel.Primitives.dll": {}, - "ref/netstandard2.0/System.ServiceModel.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.ServiceModel.Primitives.dll": {}, - "lib/netstandard2.0/System.ServiceModel.dll": {} - } - }, - "System.Text.Encoding/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Text.Encoding.dll": {} - } - }, - "System.Text.Encoding.CodePages/4.5.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "2.0.0", - "System.Runtime.CompilerServices.Unsafe": "4.5.0" - }, - "compile": { - "ref/netstandard2.0/_._": {} - }, - "runtime": { - "lib/netstandard2.0/System.Text.Encoding.CodePages.dll": {} - }, - "runtimeTargets": { - "runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Text.Encoding.Extensions/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0", - "System.Text.Encoding": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Text.Encoding.Extensions.dll": {} - } - }, - "System.Text.Encodings.Web/4.5.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Text.Encodings.Web.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Text.Encodings.Web.dll": {} - } - }, - "System.Text.RegularExpressions/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netcoreapp1.1/System.Text.RegularExpressions.dll": {} - }, - "runtime": { - "lib/netstandard1.6/System.Text.RegularExpressions.dll": {} - } - }, - "System.Threading/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Threading.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Threading.dll": {} - } - }, - "System.Threading.Channels/4.5.0": { - "type": "package", - "compile": { - "lib/netcoreapp2.1/System.Threading.Channels.dll": {} - }, - "runtime": { - "lib/netcoreapp2.1/System.Threading.Channels.dll": {} - } - }, - "System.Threading.Overlapped/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtimeTargets": { - "runtimes/unix/lib/netstandard1.3/System.Threading.Overlapped.dll": { - "assetType": "runtime", - "rid": "unix" - }, - "runtimes/win/lib/netstandard1.3/System.Threading.Overlapped.dll": { - "assetType": "runtime", - "rid": "win" - } - } - }, - "System.Threading.Tasks/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Threading.Tasks.dll": {} - } - }, - "System.Threading.Tasks.Dataflow/4.9.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/System.Threading.Tasks.Dataflow.dll": {} - }, - "runtime": { - "lib/netstandard2.0/System.Threading.Tasks.Dataflow.dll": {} - } - }, - "System.Threading.Tasks.Extensions/4.5.2": { - "type": "package", - "compile": { - "ref/netcoreapp2.1/_._": {} - }, - "runtime": { - "lib/netcoreapp2.1/_._": {} - } - }, - "System.Threading.Tasks.Parallel/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections.Concurrent": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tracing": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Threading.Tasks": "4.3.0" - }, - "compile": { - "ref/netstandard1.1/System.Threading.Tasks.Parallel.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Threading.Tasks.Parallel.dll": {} - } - }, - "System.Threading.Thread/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtime": { - "lib/netstandard1.3/System.Threading.Thread.dll": {} - } - }, - "System.Threading.ThreadPool/4.3.0": { - "type": "package", - "dependencies": { - "System.Runtime": "4.3.0", - "System.Runtime.Handles": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtime": { - "lib/netstandard1.3/System.Threading.ThreadPool.dll": {} - } - }, - "System.Threading.Timer/4.3.0": { - "type": "package", - "dependencies": { - "Microsoft.NETCore.Platforms": "1.1.0", - "Microsoft.NETCore.Targets": "1.1.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "ref/netstandard1.2/System.Threading.Timer.dll": {} - } - }, - "System.ValueTuple/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0" - }, - "compile": { - "lib/netstandard1.0/System.ValueTuple.dll": {} - }, - "runtime": { - "lib/netstandard1.0/System.ValueTuple.dll": {} - } - }, - "System.Xml.ReaderWriter/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.IO.FileSystem": "4.3.0", - "System.IO.FileSystem.Primitives": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Runtime.InteropServices": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Text.Encoding.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading.Tasks": "4.3.0", - "System.Threading.Tasks.Extensions": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Xml.ReaderWriter.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Xml.ReaderWriter.dll": {} - } - }, - "System.Xml.XDocument/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Diagnostics.Tools": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Reflection": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Xml.XDocument.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Xml.XDocument.dll": {} - } - }, - "System.Xml.XmlDocument/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.Encoding": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/System.Xml.XmlDocument.dll": {} - }, - "runtime": { - "lib/netstandard1.3/System.Xml.XmlDocument.dll": {} - } - }, - "System.Xml.XmlSerializer/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Linq": "4.3.0", - "System.Reflection": "4.3.0", - "System.Reflection.Emit": "4.3.0", - "System.Reflection.Emit.ILGeneration": "4.3.0", - "System.Reflection.Extensions": "4.3.0", - "System.Reflection.Primitives": "4.3.0", - "System.Reflection.TypeExtensions": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Text.RegularExpressions": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XmlDocument": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtime": { - "lib/netstandard1.3/System.Xml.XmlSerializer.dll": {} - } - }, - "System.Xml.XPath/4.3.0": { - "type": "package", - "dependencies": { - "System.Collections": "4.3.0", - "System.Diagnostics.Debug": "4.3.0", - "System.Globalization": "4.3.0", - "System.IO": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtime": { - "lib/netstandard1.3/System.Xml.XPath.dll": {} - } - }, - "System.Xml.XPath.XDocument/4.3.0": { - "type": "package", - "dependencies": { - "System.Diagnostics.Debug": "4.3.0", - "System.Linq": "4.3.0", - "System.Resources.ResourceManager": "4.3.0", - "System.Runtime": "4.3.0", - "System.Runtime.Extensions": "4.3.0", - "System.Threading": "4.3.0", - "System.Xml.ReaderWriter": "4.3.0", - "System.Xml.XDocument": "4.3.0", - "System.Xml.XPath": "4.3.0" - }, - "compile": { - "ref/netstandard1.3/_._": {} - }, - "runtime": { - "lib/netstandard1.3/System.Xml.XPath.XDocument.dll": {} - } - }, - "Tmds.LibC/0.2.0": { - "type": "package", - "compile": { - "ref/netstandard2.0/Tmds.LibC.dll": {} - }, - "runtimeTargets": { - "runtimes/linux-arm/lib/netstandard2.0/Tmds.LibC.dll": { - "assetType": "runtime", - "rid": "linux-arm" - }, - "runtimes/linux-arm64/lib/netstandard2.0/Tmds.LibC.dll": { - "assetType": "runtime", - "rid": "linux-arm64" - }, - "runtimes/linux-x64/lib/netstandard2.0/Tmds.LibC.dll": { - "assetType": "runtime", - "rid": "linux-x64" - } - } - }, - "Catalyst.Abstractions/0.1.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "Catalyst.Protocol": "1.0.0", - "DnsClient": "1.2.0", - "DotNetty.Transport": "0.6.0", - "Google.Protobuf": "3.9.1", - "Ipfs.HttpGateway": "0.4.0", - "Microsoft.Extensions.Configuration.Abstractions": "2.2.0", - "Multiformats.Hash": "1.5.0", - "Nethermind.Dirichlet.Numerics": "1.0.0", - "Nethermind.Evm": "1.0.0", - "SharpRepository.Repository": "2.0.4.6", - "System.IO.Abstractions": "6.0.3", - "System.Reactive": "4.1.6" - }, - "compile": { - "bin/placeholder/Catalyst.Abstractions.dll": {} - }, - "runtime": { - "bin/placeholder/Catalyst.Abstractions.dll": {} - } - }, - "Catalyst.Core.Lib/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "Autofac": "4.8.1", - "Autofac.Configuration": "4.1.0", - "AutofacSerilogIntegration": "2.0.0", - "Catalyst.Abstractions": "0.1.0", - "Dawn.Guard": "1.9.0", - "DotNetty.Codecs": "0.6.0", - "DotNetty.Codecs.Protobuf": "0.6.0", - "DotNetty.Handlers": "0.6.0", - "Google.Protobuf": "3.9.1", - "MongoDB.Bson": "2.7.0", - "Multiformats.Hash": "1.5.0", - "Nethereum.RLP": "3.3.0", - "Polly": "7.1.0", - "Serilog.Enrichers.Environment": "2.1.3", - "Serilog.Enrichers.Thread": "3.1.0", - "Serilog.Extensions.Logging": "2.0.4", - "Serilog.Settings.Configuration": "3.1.0", - "Serilog.Sinks.Console": "3.1.1", - "Serilog.Sinks.File": "4.0.0", - "SharpRepository.Ioc.Autofac": "2.0.4.2", - "SharpRepository.XmlRepository": "2.0.1-alpha3", - "SimpleBase": "1.3.1" - }, - "compile": { - "bin/placeholder/Catalyst.Core.Lib.dll": {} - }, - "runtime": { - "bin/placeholder/Catalyst.Core.Lib.dll": {} - } - }, - "Catalyst.Protocol/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "Dawn.Guard": "1.9.0", - "Google.Protobuf": "3.9.1" - }, - "compile": { - "bin/placeholder/Catalyst.Protocol.dll": {} - }, - "runtime": { - "bin/placeholder/Catalyst.Protocol.dll": {} - } - }, - "Nethermind.Core/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "Extensions.Data.xxHash.core20": "1.0.2.1", - "Microsoft.IO.RecyclableMemoryStream": "1.2.2", - "Nethermind.Dirichlet.Numerics": "1.0.0", - "Nethermind.HashLib": "1.0.0", - "Nethermind.Logging": "1.0.0", - "Nethermind.Secp256k1": "1.0.0", - "Newtonsoft.Json": "12.0.2" - }, - "compile": { - "bin/placeholder/Nethermind.Core.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Core.dll": {} - } - }, - "Nethermind.Dirichlet.Numerics/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "compile": { - "bin/placeholder/Nethermind.Dirichlet.Numerics.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Dirichlet.Numerics.dll": {} - } - }, - "Nethermind.Evm/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "Microsoft.IO.RecyclableMemoryStream": "1.2.2", - "Nethermind.Core": "1.0.0", - "Nethermind.Dirichlet.Numerics": "1.0.0", - "Nethermind.Store": "1.0.0", - "System.Buffers": "4.5.0" - }, - "compile": { - "bin/placeholder/Nethermind.Evm.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Evm.dll": {} - } - }, - "Nethermind.HashLib/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "System.Security.Cryptography.Algorithms": "4.3.1" - }, - "compile": { - "bin/placeholder/Nethermind.HashLib.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.HashLib.dll": {} - } - }, - "Nethermind.Logging/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "NLog": "4.6.5", - "NLog.StructuredLogging.Json": "4.0.0", - "NLog.Targets.Seq": "1.1.0", - "NLog.Targets.Syslog": "5.1.0" - }, - "compile": { - "bin/placeholder/Nethermind.Logging.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Logging.dll": {} - } - }, - "Nethermind.Secp256k1/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "compile": { - "bin/placeholder/Nethermind.Secp256k1.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Secp256k1.dll": {} - } - }, - "Nethermind.Store/1.0.0": { - "type": "project", - "framework": ".NETCoreApp,Version=v2.2", - "dependencies": { - "Nethermind.Core": "1.0.0" - }, - "compile": { - "bin/placeholder/Nethermind.Store.dll": {} - }, - "runtime": { - "bin/placeholder/Nethermind.Store.dll": {} - } - } - } - }, - "libraries": { - "Autofac/4.8.1": { - "sha512": "aIT9rupCOdab5RMfxvWTBmOxGU77tLqmvSF4V89SzV6oQcJrtuKw/Xp55xy9EijSktbMka55SbroAPOyT+lziw==", - "type": "package", - "path": "autofac/4.8.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "autofac.4.8.1.nupkg.sha512", - "autofac.nuspec", - "lib/net45/Autofac.dll", - "lib/net45/Autofac.xml", - "lib/netstandard1.1/Autofac.dll", - "lib/netstandard1.1/Autofac.xml" - ] - }, - "Autofac.Configuration/4.1.0": { - "sha512": "0RBau4Z2V7BYkU3m62cMCOzKjPFakSDQ91aAvLxo/IeafS+2SdRh50B0dVpLYyOfk6JgX63eWvJJXOQ/VT/7Kw==", - "type": "package", - "path": "autofac.configuration/4.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "autofac.configuration.4.1.0.nupkg.sha512", - "autofac.configuration.nuspec", - "lib/net45/Autofac.Configuration.dll", - "lib/net45/Autofac.Configuration.pdb", - "lib/net45/Autofac.Configuration.xml", - "lib/netstandard1.3/Autofac.Configuration.dll", - "lib/netstandard1.3/Autofac.Configuration.pdb", - "lib/netstandard1.3/Autofac.Configuration.xml", - "lib/netstandard2.0/Autofac.Configuration.dll", - "lib/netstandard2.0/Autofac.Configuration.pdb", - "lib/netstandard2.0/Autofac.Configuration.xml" - ] - }, - "Autofac.Extensions.DependencyInjection/4.3.1": { - "sha512": "bwHFzDIxpLxZyTWaNDEaOe4mXDtrP+RNxCtP7lQ5m40syPLqYAUMWfZfi0M0pAGukH+zmd8SqOwbQeDRNMihLA==", - "type": "package", - "path": "autofac.extensions.dependencyinjection/4.3.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "autofac.extensions.dependencyinjection.4.3.1.nupkg.sha512", - "autofac.extensions.dependencyinjection.nuspec", - "lib/netstandard1.1/Autofac.Extensions.DependencyInjection.dll", - "lib/netstandard1.1/Autofac.Extensions.DependencyInjection.pdb", - "lib/netstandard1.1/Autofac.Extensions.DependencyInjection.xml", - "lib/netstandard2.0/Autofac.Extensions.DependencyInjection.dll", - "lib/netstandard2.0/Autofac.Extensions.DependencyInjection.pdb", - "lib/netstandard2.0/Autofac.Extensions.DependencyInjection.xml" - ] - }, - "AutofacSerilogIntegration/2.0.0": { - "sha512": "m8IjK01G1WUPgj6hEhE5mXjnBc6q+gK39vdFEFb4fzJ4bYOQv3m0pf1Z0Sg6DninRzm/NR9zGjLuyiJ8ixsoWA==", - "type": "package", - "path": "autofacserilogintegration/2.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "autofacserilogintegration.2.0.0.nupkg.sha512", - "autofacserilogintegration.nuspec", - "lib/net45/AutofacSerilogIntegration.dll", - "lib/netstandard1.1/AutofacSerilogIntegration.dll" - ] - }, - "BinaryEncoding/1.4.0": { - "sha512": "1cnkP90c+zNcRyabjKSA3VYJvpYfkGEpXeekfF8KdTFo3VyUUFOioAsANbG8nsMyedGcmUOqHWd1d3fOXke4VA==", - "type": "package", - "path": "binaryencoding/1.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "binaryencoding.1.4.0.nupkg.sha512", - "binaryencoding.nuspec", - "lib/net452/BinaryEncoding.dll", - "lib/net452/BinaryEncoding.runtimeconfig.json", - "lib/netstandard1.1/BinaryEncoding.dll", - "lib/netstandard1.1/BinaryEncoding.runtimeconfig.json" - ] - }, - "Common.Logging/3.4.1": { - "sha512": "5eZ/vgEOqzLg4PypZqnJ+wMhhgHyckicbZY4iDxqQ4FtOz0CpdYZ0xQ78aszMzeAJZiLLb5VdR9tPfunVQLz6g==", - "type": "package", - "path": "common.logging/3.4.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "common.logging.3.4.1.nupkg.sha512", - "common.logging.nuspec", - "lib/net35/Common.Logging.dll", - "lib/net35/Common.Logging.pdb", - "lib/net35/Common.Logging.xml", - "lib/net40/Common.Logging.dll", - "lib/net40/Common.Logging.pdb", - "lib/net40/Common.Logging.xml", - "lib/netstandard1.3/Common.Logging.dll", - "lib/netstandard1.3/Common.Logging.pdb", - "lib/netstandard1.3/Common.Logging.xml" - ] - }, - "Common.Logging.Core/3.4.1": { - "sha512": "wLHldZHvxsSD6Ahonfj00/SkfHfKqO+YT6jsUwVm8Rch1REL9IArHAcSLXxYxYfu5/4ydGtmXvOtaH3AkVPu0A==", - "type": "package", - "path": "common.logging.core/3.4.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "common.logging.core.3.4.1.nupkg.sha512", - "common.logging.core.nuspec", - "lib/net40/Common.Logging.Core.XML", - "lib/net40/Common.Logging.Core.dll", - "lib/net40/Common.Logging.Core.pdb", - "lib/netstandard1.0/Common.Logging.Core.dll", - "lib/netstandard1.0/Common.Logging.Core.pdb", - "lib/netstandard1.0/Common.Logging.Core.xml", - "lib/portable-win+net40+sl40+wp7+wpa81/Common.Logging.Core.XML", - "lib/portable-win+net40+sl40+wp7+wpa81/Common.Logging.Core.dll", - "lib/portable-win+net40+sl40+wp7+wpa81/Common.Logging.Core.pdb" - ] - }, - "Dawn.Guard/1.9.0": { - "sha512": "xqenw7h4BHYMlCK9IeMNHa+db+kfjvnfGrqABHnNT053EvE6rYQS545Q+wTEU5sBZiEW7CvcThWm/pdWalu3cg==", - "type": "package", - "path": "dawn.guard/1.9.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dawn.guard.1.9.0.nupkg.sha512", - "dawn.guard.nuspec", - "lib/netstandard1.0/Dawn.Guard.dll", - "lib/netstandard1.0/Dawn.Guard.xml", - "lib/netstandard2.0/Dawn.Guard.dll", - "lib/netstandard2.0/Dawn.Guard.xml" - ] - }, - "DnsClient/1.2.0": { - "sha512": "P34wUkeqU4FoQiOMV8OjpdeDZKs4d3r+VlHuKJ6eO5feiZgna3+9MF5orHRUn3DAv1g/HPE5hlkGucmxmsFfBw==", - "type": "package", - "path": "dnsclient/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dnsclient.1.2.0.nupkg.sha512", - "dnsclient.nuspec", - "lib/net45/DnsClient.dll", - "lib/net45/DnsClient.xml", - "lib/net471/DnsClient.dll", - "lib/net471/DnsClient.xml", - "lib/netstandard1.3/DnsClient.dll", - "lib/netstandard1.3/DnsClient.xml", - "lib/netstandard2.0/DnsClient.dll", - "lib/netstandard2.0/DnsClient.xml" - ] - }, - "DotNetty.Buffers/0.6.0": { - "sha512": "MjxoFMdKsSJ5CLKTFHTKYoFUZUvC6VHGx71vPLSflmR/X4dSm57/hVxhsQY3YU6aASeRjqgNiCHn4II+UNOIWQ==", - "type": "package", - "path": "dotnetty.buffers/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.buffers.0.6.0.nupkg.sha512", - "dotnetty.buffers.nuspec", - "lib/net45/DotNetty.Buffers.dll", - "lib/net45/DotNetty.Buffers.xml", - "lib/netstandard1.3/DotNetty.Buffers.dll", - "lib/netstandard1.3/DotNetty.Buffers.xml" - ] - }, - "DotNetty.Codecs/0.6.0": { - "sha512": "gLAVaII7A4er3ZwUJIAZpynDLsxNr7Acw61OoCDfzIAkcB2L6OJ9jPnKy8YtrtTnJXm0lNod5+7aRJTmaBbT0Q==", - "type": "package", - "path": "dotnetty.codecs/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.codecs.0.6.0.nupkg.sha512", - "dotnetty.codecs.nuspec", - "lib/net45/DotNetty.Codecs.dll", - "lib/net45/DotNetty.Codecs.xml", - "lib/netstandard1.3/DotNetty.Codecs.dll", - "lib/netstandard1.3/DotNetty.Codecs.xml" - ] - }, - "DotNetty.Codecs.Protobuf/0.6.0": { - "sha512": "j0feAIaVVlvWwWg0t1q8s1hjccS8ekTcS9gHyO2oEcuzwPKRynKyA/Kn7Lw5GqfMF0NgMHc3GdvesIh64R7KOQ==", - "type": "package", - "path": "dotnetty.codecs.protobuf/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.codecs.protobuf.0.6.0.nupkg.sha512", - "dotnetty.codecs.protobuf.nuspec", - "lib/net45/DotNetty.Codecs.Protobuf.dll", - "lib/net45/DotNetty.Codecs.Protobuf.xml", - "lib/netstandard1.3/DotNetty.Codecs.Protobuf.dll", - "lib/netstandard1.3/DotNetty.Codecs.Protobuf.xml" - ] - }, - "DotNetty.Common/0.6.0": { - "sha512": "aVD0bWoaH+Ou0wOTFzYEs3hrzsckUEkbdweVgwHsk7q0OTPgmPD/QkGxjNqjZ9diUyeGvHzYn7mFQGG5cgbYUA==", - "type": "package", - "path": "dotnetty.common/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.common.0.6.0.nupkg.sha512", - "dotnetty.common.nuspec", - "lib/net45/DotNetty.Common.dll", - "lib/net45/DotNetty.Common.xml", - "lib/netstandard1.3/DotNetty.Common.dll", - "lib/netstandard1.3/DotNetty.Common.xml" - ] - }, - "DotNetty.Handlers/0.6.0": { - "sha512": "LXj49DSk/PzC5r7MkXKpYit4t5DsUNpEw1m899SoV2gGVs9WooYUVyjOhKwyqBUPie4QgkYc6tsgAHNJUmDhbA==", - "type": "package", - "path": "dotnetty.handlers/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.handlers.0.6.0.nupkg.sha512", - "dotnetty.handlers.nuspec", - "lib/net45/DotNetty.Handlers.dll", - "lib/net45/DotNetty.Handlers.xml", - "lib/netstandard1.3/DotNetty.Handlers.dll", - "lib/netstandard1.3/DotNetty.Handlers.xml" - ] - }, - "DotNetty.Transport/0.6.0": { - "sha512": "HLYcbZfLDIVPhtr+x3nv2vvxBxKONWXeP/B1LaXvPeSfdvlOtvoxB0tuv92SaOdqq9NUCjAP59Nn9xofRrt8mA==", - "type": "package", - "path": "dotnetty.transport/0.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dotnetty.transport.0.6.0.nupkg.sha512", - "dotnetty.transport.nuspec", - "lib/net45/DotNetty.Transport.dll", - "lib/net45/DotNetty.Transport.xml", - "lib/netstandard1.3/DotNetty.Transport.dll", - "lib/netstandard1.3/DotNetty.Transport.xml" - ] - }, - "Extensions.Data.xxHash.core20/1.0.2.1": { - "sha512": "gunLYoUUFQRcw2yM4eROw5YG6jZsEsoIj6BdKhU2kISNLoQ0IekknFZ42NXYWAkjH9D4F5YlwUUH2XJIkgeUfw==", - "type": "package", - "path": "extensions.data.xxhash.core20/1.0.2.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "extensions.data.xxhash.core20.1.0.2.1.nupkg.sha512", - "extensions.data.xxhash.core20.nuspec", - "lib/netstandard2.0/Extensions.Data.xxHash.core20.dll" - ] - }, - "Google.Protobuf/3.9.1": { - "sha512": "qCs2Tn+hJSllwIjQ2HQ4uNYGLbXWr3D6jJPFlJkIu1pErk4eQB1qlUcjA7NxA4JwOJzQbkZL45EbPGT/ZgwtsQ==", - "type": "package", - "path": "google.protobuf/3.9.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "google.protobuf.3.9.1.nupkg.sha512", - "google.protobuf.nuspec", - "lib/net45/Google.Protobuf.dll", - "lib/net45/Google.Protobuf.pdb", - "lib/net45/Google.Protobuf.xml", - "lib/netstandard1.0/Google.Protobuf.dll", - "lib/netstandard1.0/Google.Protobuf.pdb", - "lib/netstandard1.0/Google.Protobuf.xml", - "lib/netstandard2.0/Google.Protobuf.dll", - "lib/netstandard2.0/Google.Protobuf.pdb", - "lib/netstandard2.0/Google.Protobuf.xml" - ] - }, - "Ipfs.Core/0.51.1": { - "sha512": "tYIqY2DbxB0sAc/AcjQaeqaalrAcSCgnAtCXLr8VtB7lMg+LBjBwZgpNrDU1+fPhRQTEYmBpUy1Yzv/5ao35fw==", - "type": "package", - "path": "ipfs.core/0.51.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ipfs.core.0.51.1.nupkg.sha512", - "ipfs.core.nuspec", - "lib/net45/Ipfs.Core.dll", - "lib/net45/Ipfs.Core.xml", - "lib/netstandard1.4/Ipfs.Core.dll", - "lib/netstandard1.4/Ipfs.Core.xml", - "lib/netstandard2.0/Ipfs.Core.dll", - "lib/netstandard2.0/Ipfs.Core.xml" - ] - }, - "Ipfs.Engine/0.9.1": { - "sha512": "bnkS/HG5qMPmhsdrUSOBO2ZvROI0pLsatIAYQuhAAMeqAo06ji4t4gc2VZoeDcgVqOJyjZzKbBLmi7Lkw69eSg==", - "type": "package", - "path": "ipfs.engine/0.9.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ipfs.engine.0.9.1.nupkg.sha512", - "ipfs.engine.nuspec", - "lib/net461/Ipfs.Engine.dll", - "lib/net461/Ipfs.Engine.xml", - "lib/netstandard2.0/Ipfs.Engine.dll", - "lib/netstandard2.0/Ipfs.Engine.xml" - ] - }, - "Ipfs.HttpGateway/0.4.0": { - "sha512": "6Jl1gP5qVmbQAqSc7JLuTMw4pa2k+PVqpMKTnuOpCNyQBi508kQ/IU1DgVHlUsUyhZLxGIn+BHCxHPKk5tD2Rg==", - "type": "package", - "path": "ipfs.httpgateway/0.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ipfs.httpgateway.0.4.0.nupkg.sha512", - "ipfs.httpgateway.nuspec", - "lib/netcoreapp2.2/Ipfs.HttpGateway.Views.dll", - "lib/netcoreapp2.2/Ipfs.HttpGateway.dll", - "lib/netcoreapp2.2/Ipfs.HttpGateway.runtimeconfig.json", - "lib/netcoreapp2.2/Ipfs.HttpGateway.xml" - ] - }, - "IPNetwork2/2.1.2": { - "sha512": "DZF2SbtyqukkLSzyu0KqcXUBdhDXQ/K3QWM27vlvdPo3W+iI81pgUCNTwyynNyVc5Q9AIn0znFVVhHGbhiofUg==", - "type": "package", - "path": "ipnetwork2/2.1.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ipnetwork2.2.1.2.nupkg.sha512", - "ipnetwork2.nuspec", - "lib/net40/System.Net.IPNetwork.dll", - "lib/net45/System.Net.IPNetwork.dll", - "lib/net46/System.Net.IPNetwork.dll", - "lib/netstandard1.3/System.Net.IPNetwork.dll" - ] - }, - "Makaretu.Dns/1.4.1": { - "sha512": "mSGwiWrYV5XZsOl/bxAZ8Np/xHYTH2PE66Skh3iwR/g5iXGN2aH+wmzAnQo5XddKiTaU+5aXQbPThMN1K83bkA==", - "type": "package", - "path": "makaretu.dns/1.4.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Makaretu.Dns.dll", - "lib/net45/Makaretu.Dns.xml", - "lib/net472/Makaretu.Dns.dll", - "lib/net472/Makaretu.Dns.xml", - "lib/netstandard1.4/Makaretu.Dns.dll", - "lib/netstandard1.4/Makaretu.Dns.xml", - "lib/netstandard2.0/Makaretu.Dns.dll", - "lib/netstandard2.0/Makaretu.Dns.xml", - "makaretu.dns.1.4.1.nupkg.sha512", - "makaretu.dns.nuspec" - ] - }, - "Makaretu.Dns.Multicast/0.18.0": { - "sha512": "r6t6JlyFAdVVbSVpOljlDlaG6OhDi7988bnET9+6e9GrUXvvsPOyRQZ9j6k+ZJW6ZXrFrKuJlm4pRCgTDUq/Zg==", - "type": "package", - "path": "makaretu.dns.multicast/0.18.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net461/Makaretu.Dns.Multicast.dll", - "lib/net461/Makaretu.Dns.Multicast.xml", - "lib/netstandard1.4/Makaretu.Dns.Multicast.dll", - "lib/netstandard1.4/Makaretu.Dns.Multicast.xml", - "lib/netstandard2.0/Makaretu.Dns.Multicast.dll", - "lib/netstandard2.0/Makaretu.Dns.Multicast.xml", - "makaretu.dns.multicast.0.18.0.nupkg.sha512", - "makaretu.dns.multicast.nuspec" - ] - }, - "Makaretu.Dns.Unicast/0.9.0": { - "sha512": "bXxQUL9GyWsk4dr4k7k4JD4YhZterw8vvUf4oCUfmNXKcInTDvWsbL13SfZ4hn9AxXmOYvfO88tEnmxFDG+/Mw==", - "type": "package", - "path": "makaretu.dns.unicast/0.9.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net46/Makaretu.Dns.Unicast.dll", - "lib/net46/Makaretu.Dns.Unicast.xml", - "lib/netstandard1.4/Makaretu.Dns.Unicast.dll", - "lib/netstandard1.4/Makaretu.Dns.Unicast.xml", - "lib/netstandard2.0/Makaretu.Dns.Unicast.dll", - "lib/netstandard2.0/Makaretu.Dns.Unicast.xml", - "makaretu.dns.unicast.0.9.0.nupkg.sha512", - "makaretu.dns.unicast.nuspec" - ] - }, - "Makaretu.KBucket/0.5.0": { - "sha512": "SqCio1JHp3CAtfqNnsuhe4wQdPZDv2yAco0cW0uW9KfL3AC0clGsQCmzKAr2VvFaUkGMysB7HmWqzf6i0Zs3Fg==", - "type": "package", - "path": "makaretu.kbucket/0.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Makaretu.KBucket.dll", - "lib/net45/Makaretu.KBucket.xml", - "lib/netstandard1.4/Makaretu.KBucket.dll", - "lib/netstandard1.4/Makaretu.KBucket.xml", - "lib/netstandard2.0/Makaretu.KBucket.dll", - "lib/netstandard2.0/Makaretu.KBucket.xml", - "makaretu.kbucket.0.5.0.nupkg.sha512", - "makaretu.kbucket.nuspec" - ] - }, - "Microsoft.AspNet.WebApi.Client/5.2.4": { - "sha512": "UhIq4MHMxrbxutzzTSlfmScJ+phRGQlSoPAzsKhDqMTHzJ0GuxTGlplacHiEST6W0rdcD1Zqrx2njXyQQh+xug==", - "type": "package", - "path": "microsoft.aspnet.webapi.client/5.2.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/System.Net.Http.Formatting.dll", - "lib/net45/System.Net.Http.Formatting.xml", - "lib/netstandard2.0/System.Net.Http.Formatting.dll", - "lib/netstandard2.0/System.Net.Http.Formatting.xml", - "lib/portable-wp8+netcore45+net45+wp81+wpa81/System.Net.Http.Formatting.dll", - "lib/portable-wp8+netcore45+net45+wp81+wpa81/System.Net.Http.Formatting.xml", - "microsoft.aspnet.webapi.client.5.2.4.nupkg.sha512", - "microsoft.aspnet.webapi.client.nuspec" - ] - }, - "Microsoft.AspNetCore/2.2.0": { - "sha512": "kkktm2qclx/caMJcwxMstxcowWgUGR4gvKdJwb2Z2mPa8zeOfWwc30Ou0S6ZBJUB6Nre/2ub6tAylMJURpkjhg==", - "type": "package", - "path": "microsoft.aspnetcore/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.xml", - "microsoft.aspnetcore.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.nuspec" - ] - }, - "Microsoft.AspNetCore.Antiforgery/2.2.0": { - "sha512": "Z3NuQZ+nJvw5NGsDXKwlfJoXoHESXESd7dySekp+BMGusbb7f18eISRrgqabmMLQzAetVIoXm3HC00g4Xc8r/A==", - "type": "package", - "path": "microsoft.aspnetcore.antiforgery/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Antiforgery.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Antiforgery.xml", - "microsoft.aspnetcore.antiforgery.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.antiforgery.nuspec" - ] - }, - "Microsoft.AspNetCore.App/2.1.0": { - "sha512": "O0k+hAxLPrIARV8qqIBhWvzjVxmGG3WOHr6ShInE30gCepEhwnaYrLXyChQs/DEoO6m0WpBG9kLD/WdBy1CYFg==", - "type": "package", - "path": "microsoft.aspnetcore.app/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/netcoreapp2.1/Microsoft.AspNetCore.App.props", - "build/netcoreapp2.1/Microsoft.AspNetCore.App.targets", - "lib/netcoreapp2.1/_._", - "microsoft.aspnetcore.app.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.app.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication/2.1.0": { - "sha512": "KxFgQ60DnBdbKM/hofyX793k69TU+23boySySh7a3aNw5rzkxcpWzphs/O6Nxo5j0kPcZBkpcA4KSDTpPyD8dQ==", - "type": "package", - "path": "microsoft.aspnetcore.authentication/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.xml", - "microsoft.aspnetcore.authentication.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication.Abstractions/2.2.0": { - "sha512": "Z60lbge9Va4A6EUME3YX8RuGEmmzkZA1mExxUjfrqb/3GlmcpZyMDQe2l4/UmEACdKSQekCUIyPV46rUE70rWA==", - "type": "package", - "path": "microsoft.aspnetcore.authentication.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Abstractions.xml", - "microsoft.aspnetcore.authentication.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication.Cookies/2.1.0": { - "sha512": "a7OBpNPf+f82ty+tQkVWs6yrBBV3MdVrTrFxbC+sNdWBeDP2yrDqy+7ndP/iAXtADpH7vyj2VLJYIZlle/xYLQ==", - "type": "package", - "path": "microsoft.aspnetcore.authentication.cookies/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Cookies.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Cookies.xml", - "microsoft.aspnetcore.authentication.cookies.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.cookies.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication.Core/2.2.0": { - "sha512": "UxOqkyDMaM2X1PJaB5a0fO86SqBYhxZuUser6qZ+H78gLqNa4Faqkl78IcEWV19oVqrCY0EFNOj+2gXWPk2Faw==", - "type": "package", - "path": "microsoft.aspnetcore.authentication.core/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Core.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Core.xml", - "microsoft.aspnetcore.authentication.core.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.core.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication.Facebook/2.1.0": { - "sha512": "vp5ZFVlLrh0i8Rn6Nw9zmcjdQ8FCFTRvI8Pl88N2l2ZLIN0/Num0cJ9FNKgP7TrDT6GcqiG4iYqvzoU7qFB4XA==", - "type": "package", - "path": "microsoft.aspnetcore.authentication.facebook/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Facebook.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Facebook.xml", - "microsoft.aspnetcore.authentication.facebook.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.facebook.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication.Google/2.1.0": { - "sha512": "PLLDn3CmhXK8HEXIkX+KC5ggzfhPar3TLuEteye1oVmpZXE9gdypymL3FlBbtl2dJl3w5c0ldTRGG0MysJKU1A==", - "type": "package", - "path": "microsoft.aspnetcore.authentication.google/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Google.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Google.xml", - "microsoft.aspnetcore.authentication.google.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.google.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication.JwtBearer/2.1.0": { - "sha512": "j2wnthIENdMUXVGbRqe4CiuazXK7y8DYHs9Mo2hyX0jtfaf8q+afg8AZ1fTx0McaysXkCSStkM99ncqHGlREGg==", - "type": "package", - "path": "microsoft.aspnetcore.authentication.jwtbearer/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.JwtBearer.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.JwtBearer.xml", - "microsoft.aspnetcore.authentication.jwtbearer.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.jwtbearer.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication.MicrosoftAccount/2.1.0": { - "sha512": "cjSi48Ae0Ko0NuKWA3zO/Tondgi4XG27Gr6NxHp+vYF4UbkeEuQ4eD18U2oGXCiIWsx2JWj9P/aRI7pPX/5Lrg==", - "type": "package", - "path": "microsoft.aspnetcore.authentication.microsoftaccount/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.MicrosoftAccount.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.MicrosoftAccount.xml", - "microsoft.aspnetcore.authentication.microsoftaccount.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.microsoftaccount.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication.OAuth/2.1.0": { - "sha512": "A8e0ANSZyMEnKAdq5JYOth/4Z/zv9062onQy/LfFW/HfhmwYKHuTVoKO1eohyimAyZAgpXADykp5DVq7D+6SHg==", - "type": "package", - "path": "microsoft.aspnetcore.authentication.oauth/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.OAuth.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.OAuth.xml", - "microsoft.aspnetcore.authentication.oauth.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.oauth.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication.OpenIdConnect/2.1.0": { - "sha512": "e1ysJ72TxK/fLzyS65NFAtGedKCQu9yD3ltr9VX7ZO+zkB361S0/o5GHnzy9nmhyQ8dPIaxBmFES2nPWuaMgWA==", - "type": "package", - "path": "microsoft.aspnetcore.authentication.openidconnect/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.OpenIdConnect.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.OpenIdConnect.xml", - "microsoft.aspnetcore.authentication.openidconnect.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.openidconnect.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication.Twitter/2.1.0": { - "sha512": "W2OipR5dYBBLFEPHffwhBaTFSW/gviL8UPIx5KrlOlpAJ+usilLTIITNv76VHfA1yNwPQ39RpG/D0YYdkA/vew==", - "type": "package", - "path": "microsoft.aspnetcore.authentication.twitter/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Twitter.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.Twitter.xml", - "microsoft.aspnetcore.authentication.twitter.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.twitter.nuspec" - ] - }, - "Microsoft.AspNetCore.Authentication.WsFederation/2.1.0": { - "sha512": "HKT+FLjbRiUktKyO852R09i860drPwMs8r1zrxSxW/8K6PrWkm3M0PzetPKlZe0/g94Xkxlb/7WyH7Fsa/OcmQ==", - "type": "package", - "path": "microsoft.aspnetcore.authentication.wsfederation/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.WsFederation.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authentication.WsFederation.xml", - "microsoft.aspnetcore.authentication.wsfederation.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.authentication.wsfederation.nuspec" - ] - }, - "Microsoft.AspNetCore.Authorization/2.2.0": { - "sha512": "FoTvJ2jdOMR/7+uppXqjdADcsp/ZrxLVrrLuhRMr2pYTMhaLlSYw92+sLAJ6mddPCkHpHMJlFCy7bK399G3YgQ==", - "type": "package", - "path": "microsoft.aspnetcore.authorization/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.xml", - "microsoft.aspnetcore.authorization.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.authorization.nuspec" - ] - }, - "Microsoft.AspNetCore.Authorization.Policy/2.2.0": { - "sha512": "KEZYBP7SHx3TQ/H+MmqavOIojQu3d6UgNMbfafcECH3BoLIhGUDKXcyzi7hyPJUdRjV2WyoJ/Rs1moijXmjflQ==", - "type": "package", - "path": "microsoft.aspnetcore.authorization.policy/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.Policy.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Authorization.Policy.xml", - "microsoft.aspnetcore.authorization.policy.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.authorization.policy.nuspec" - ] - }, - "Microsoft.AspNetCore.Connections.Abstractions/2.2.0": { - "sha512": "Pr7Nqi4dqmcvEcQJn865oOEyGmvxz22QTxDxKLwTed3sfVXqqdn5H08ctwtFWTpdauPolko3e01gOQWJrq6/2g==", - "type": "package", - "path": "microsoft.aspnetcore.connections.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Connections.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Connections.Abstractions.xml", - "microsoft.aspnetcore.connections.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.connections.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.CookiePolicy/2.1.0": { - "sha512": "3Hi84e3ESW4MdRIFXCrJ5CZsJHEYpDQSIdVFb0Rhn4bwYapux8bRGyB9trp4HBfE6DEcIQqkA/GpeJDJADNQTw==", - "type": "package", - "path": "microsoft.aspnetcore.cookiepolicy/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.CookiePolicy.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.CookiePolicy.xml", - "microsoft.aspnetcore.cookiepolicy.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.cookiepolicy.nuspec" - ] - }, - "Microsoft.AspNetCore.Cors/2.2.0": { - "sha512": "ZjIQJCbzRS8bkVsNtv8gLfq8tzfAca8jCaU5m0ISXWnIuaK959T1/M+oKLSNqL49rEln8NwF7QcCHGMDWnXa8A==", - "type": "package", - "path": "microsoft.aspnetcore.cors/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Cors.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Cors.xml", - "microsoft.aspnetcore.cors.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.cors.nuspec" - ] - }, - "Microsoft.AspNetCore.Cryptography.Internal/2.2.0": { - "sha512": "hh/2QUBSikiZvpJUl2OkhRG+dS4p6ata+/Y/eUrXEVd/vmXSTrq4DbW1fb6SVBXhBhwQzHwrdBzcN0sUqO844w==", - "type": "package", - "path": "microsoft.aspnetcore.cryptography.internal/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.Internal.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.Internal.xml", - "microsoft.aspnetcore.cryptography.internal.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.cryptography.internal.nuspec" - ] - }, - "Microsoft.AspNetCore.Cryptography.KeyDerivation/2.1.0": { - "sha512": "iaptaaBjho0SvKrhvrNKBa8UDTPs4JTFyW78WlAs1LlR34MQ+MZCC2yeuHohomYLlAPdr0XJ5D4gP8IPMVM7Cg==", - "type": "package", - "path": "microsoft.aspnetcore.cryptography.keyderivation/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netcoreapp2.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.dll", - "lib/netcoreapp2.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.xml", - "lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Cryptography.KeyDerivation.xml", - "microsoft.aspnetcore.cryptography.keyderivation.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.cryptography.keyderivation.nuspec" - ] - }, - "Microsoft.AspNetCore.DataProtection/2.2.0": { - "sha512": "lICT7lE0h4+xmpfmnEvattZHU8tBrBV225otobQGpIfgf0k7V4qDV7NQ1X8YfpeUY/i8EoINpyuYh/eDXW4AnA==", - "type": "package", - "path": "microsoft.aspnetcore.dataprotection/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.xml", - "microsoft.aspnetcore.dataprotection.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.dataprotection.nuspec" - ] - }, - "Microsoft.AspNetCore.DataProtection.Abstractions/2.2.0": { - "sha512": "Z0EG2A8xrDVdNkfhYzUxzVcFKQU15qB1hn5PYtdBeJ4sWtEBOkNSa/cuaXUb/qbe6amVioKtMSXcvduCaq3Qsw==", - "type": "package", - "path": "microsoft.aspnetcore.dataprotection.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Abstractions.xml", - "microsoft.aspnetcore.dataprotection.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.dataprotection.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.DataProtection.Extensions/2.1.0": { - "sha512": "cx28IGBxsHc90zcJxLjLzhhKsDNvPsXHFvopbrUAUGMQDsZv0vSACA1NCoRjXrg6vdWpiGWac3F+rT2anwLdHQ==", - "type": "package", - "path": "microsoft.aspnetcore.dataprotection.extensions/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Extensions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.DataProtection.Extensions.xml", - "microsoft.aspnetcore.dataprotection.extensions.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.dataprotection.extensions.nuspec" - ] - }, - "Microsoft.AspNetCore.Diagnostics/2.2.0": { - "sha512": "QJHBQ6brad6JSaeJ+th6cJDfxDAWIZywFFmpeUsG42gUFWM2CIqIRlHv7oiI06PAdL0uoG5GnU+7gDDe8tjsYQ==", - "type": "package", - "path": "microsoft.aspnetcore.diagnostics/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.xml", - "microsoft.aspnetcore.diagnostics.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.diagnostics.nuspec" - ] - }, - "Microsoft.AspNetCore.Diagnostics.Abstractions/2.2.0": { - "sha512": "sBPt24dBsLBGjkoPYjqngzVOqT2acmmKgo3W2Djr7ua0PKzb+r6AeWC9Lgctv6Ba1lUX9E4vxrGOTcBiLnxLYA==", - "type": "package", - "path": "microsoft.aspnetcore.diagnostics.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.Abstractions.xml", - "microsoft.aspnetcore.diagnostics.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.diagnostics.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore/2.1.0": { - "sha512": "SmAQgo1wJVrIUyWkD0d2G3ENxDaChnYRDJUmWMZX4/PIzqOG5HW/VqcKK3pmRtGEuhBM/UW3CUbRho3Ymhwc0Q==", - "type": "package", - "path": "microsoft.aspnetcore.diagnostics.entityframeworkcore/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Diagnostics.EntityFrameworkCore.xml", - "microsoft.aspnetcore.diagnostics.entityframeworkcore.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.diagnostics.entityframeworkcore.nuspec" - ] - }, - "Microsoft.AspNetCore.HostFiltering/2.2.0": { - "sha512": "SpnsV7CwzXBa6kUr5ojRXJGehnl8c5usrABLAgTdYmwjoTyfHRZTjOdpaeIqTNkz2GTUliKFJe8PQ0eVCrhHmQ==", - "type": "package", - "path": "microsoft.aspnetcore.hostfiltering/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.HostFiltering.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.HostFiltering.xml", - "microsoft.aspnetcore.hostfiltering.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.hostfiltering.nuspec" - ] - }, - "Microsoft.AspNetCore.Hosting/2.2.0": { - "sha512": "YJmyRNkRrfjhohF9vpgSN7e3f0CS4x2VJ6VYrRCQoP82A+Rlp1SsZpkrExAvetk/acXgk5c9OyHDh7lb8GqWRg==", - "type": "package", - "path": "microsoft.aspnetcore.hosting/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.xml", - "microsoft.aspnetcore.hosting.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.hosting.nuspec" - ] - }, - "Microsoft.AspNetCore.Hosting.Abstractions/2.2.0": { - "sha512": "qQDWoHwF+ABmxTary/pEpUX7f1hCoQhk0RDnJuMlOZ+dPFp/uvk+9sxZU6w/c8EL9jPlfuYOOQJGPETUv/NNXA==", - "type": "package", - "path": "microsoft.aspnetcore.hosting.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Abstractions.xml", - "microsoft.aspnetcore.hosting.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.hosting.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Hosting.Server.Abstractions/2.2.0": { - "sha512": "p4dTiP1O/HRpwMxYATRSeekzHTv5pwh3rxPQwtbO21/xf0nbeCRswFtXQ2KJwdt7phfDZ4K9HmcCviBF1JgZLg==", - "type": "package", - "path": "microsoft.aspnetcore.hosting.server.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Hosting.Server.Abstractions.xml", - "microsoft.aspnetcore.hosting.server.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.hosting.server.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Html.Abstractions/2.2.0": { - "sha512": "NDyH+7k7iuedeOJfoUEIxUynePOPIHj5qNGnHBOenoy51HDNsdzqA4TXsjBM5/9N9nz9hzYEriHoYHc2EcxV4Q==", - "type": "package", - "path": "microsoft.aspnetcore.html.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Html.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Html.Abstractions.xml", - "microsoft.aspnetcore.html.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.html.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Http/2.2.0": { - "sha512": "Cn7ILZSQikKnJb4lhEfVxxk+CsQ36ZxVzOrzDUmqGOp9BJP3LqVYYTo/2vGgQlSo3GD4R8fDfAflX8s9vXlz/g==", - "type": "package", - "path": "microsoft.aspnetcore.http/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.xml", - "microsoft.aspnetcore.http.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.http.nuspec" - ] - }, - "Microsoft.AspNetCore.Http.Abstractions/2.2.0": { - "sha512": "0LM0wYbsvYnW11k8qh0DDWEEx/p6QaDqpcLlMyhDOmNW9KgprRh9ghTACfeOCo1hRqdktUMp6qguzOKixIL13w==", - "type": "package", - "path": "microsoft.aspnetcore.http.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Abstractions.xml", - "microsoft.aspnetcore.http.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.http.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Http.Connections/1.0.0": { - "sha512": "t6KrSJQJV/tN8Drt2S3TRAhvNqcncIbspKKPYZU3NXRgd2oM86tNTA+2sr1Kb6WEGxq9uO8HBwGtjPpegD5hRQ==", - "type": "package", - "path": "microsoft.aspnetcore.http.connections/1.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netcoreapp2.1/Microsoft.AspNetCore.Http.Connections.dll", - "lib/netcoreapp2.1/Microsoft.AspNetCore.Http.Connections.xml", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Connections.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Connections.xml", - "microsoft.aspnetcore.http.connections.1.0.0.nupkg.sha512", - "microsoft.aspnetcore.http.connections.nuspec" - ] - }, - "Microsoft.AspNetCore.Http.Connections.Common/1.0.0": { - "sha512": "LaFyFOgjjyJCQ2CF71BKoVu0R5USfidPK1vKtXVelxMeJr9QxZ6RYOuW1+3wrNRtMn7AWffiX4fBw3hebSIO8A==", - "type": "package", - "path": "microsoft.aspnetcore.http.connections.common/1.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Connections.Common.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Connections.Common.xml", - "microsoft.aspnetcore.http.connections.common.1.0.0.nupkg.sha512", - "microsoft.aspnetcore.http.connections.common.nuspec" - ] - }, - "Microsoft.AspNetCore.Http.Extensions/2.2.0": { - "sha512": "wXfB68sp3X+6/TLUHVF5eiscn9iGptGEwjJyk4yBbbOAVJECE59/KUun6sT+JlNcNCWEWE0S7Ew+yMD6bm3rrQ==", - "type": "package", - "path": "microsoft.aspnetcore.http.extensions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Extensions.xml", - "microsoft.aspnetcore.http.extensions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.http.extensions.nuspec" - ] - }, - "Microsoft.AspNetCore.Http.Features/2.2.0": { - "sha512": "jvu74/kOgIDCnAH9Xu2/wITrSagDp1L4OJ+pTrxWzOdpNc7lDtfPrywBe7h9CUkAx/895L9i3r70vf5lgoBrcw==", - "type": "package", - "path": "microsoft.aspnetcore.http.features/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Http.Features.xml", - "microsoft.aspnetcore.http.features.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.http.features.nuspec" - ] - }, - "Microsoft.AspNetCore.HttpOverrides/2.2.0": { - "sha512": "27541I282qw8FEO8nge/bIzz1kntQZlM/TuRrAtscjjsVCmb/KsV6Bi2dlVAxIqdSKLhJ84zm6Ml8NR6NJQHzw==", - "type": "package", - "path": "microsoft.aspnetcore.httpoverrides/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.HttpOverrides.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.HttpOverrides.xml", - "microsoft.aspnetcore.httpoverrides.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.httpoverrides.nuspec" - ] - }, - "Microsoft.AspNetCore.HttpsPolicy/2.1.0": { - "sha512": "6s34J8VoTC690p/2wUP+RAhG81l8s/PPU7ULCf1ybGyHrxFx6KgH9zDESDIFvA7KTditx19QAaILr4mHmPfGCA==", - "type": "package", - "path": "microsoft.aspnetcore.httpspolicy/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.HttpsPolicy.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.HttpsPolicy.xml", - "microsoft.aspnetcore.httpspolicy.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.httpspolicy.nuspec" - ] - }, - "Microsoft.AspNetCore.Identity/2.1.0": { - "sha512": "qGGS72yyE3nZcu7Tas0zmsw05oSlBi6Tqn7j/OAESzLm5VjLzdizGKexNt8bO+RJBgKB4H7upVDrKni7hQm++A==", - "type": "package", - "path": "microsoft.aspnetcore.identity/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.xml", - "microsoft.aspnetcore.identity.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.identity.nuspec" - ] - }, - "Microsoft.AspNetCore.Identity.EntityFrameworkCore/2.1.0": { - "sha512": "selZhg0DVLJDBmJwccGbuehyG4aDRPU2MprYocJPSVDTfN0h6VBSMd9OgQDU2mL4uHZbJfhn4lCzi1BQha8hoQ==", - "type": "package", - "path": "microsoft.aspnetcore.identity.entityframeworkcore/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.EntityFrameworkCore.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.EntityFrameworkCore.xml", - "microsoft.aspnetcore.identity.entityframeworkcore.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.identity.entityframeworkcore.nuspec" - ] - }, - "Microsoft.AspNetCore.Identity.UI/2.1.0": { - "sha512": "v7GC8gDTQC5zkDGJy4qGB6Xm+rUOPdJb3yOAc5BryI9IJqKC104xf7Rgo43tKFNLe6S+FkukQe5ikMjlgp7gRA==", - "type": "package", - "path": "microsoft.aspnetcore.identity.ui/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "THIRD-PARTY-NOTICES", - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.Views.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Identity.UI.dll", - "microsoft.aspnetcore.identity.ui.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.identity.ui.nuspec" - ] - }, - "Microsoft.AspNetCore.JsonPatch/2.2.0": { - "sha512": "yMA6uU+2lIxhDlrQugPcbWs+JJbehxmPQIQ+aM0Mm/GP9gx3Nve2o1xcByEKsVtIXgYTdugdidwUaQylVgr8Fg==", - "type": "package", - "path": "microsoft.aspnetcore.jsonpatch/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.JsonPatch.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.JsonPatch.xml", - "microsoft.aspnetcore.jsonpatch.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.jsonpatch.nuspec" - ] - }, - "Microsoft.AspNetCore.Localization/2.2.0": { - "sha512": "VIMUL3xHUeW0iaD4hQeY3Y8zg5abtsXAmFp3ZZxeDDv7noDKz1sfITIUx5Qh9IvXU41X49A73k77pMJ4OkyvGA==", - "type": "package", - "path": "microsoft.aspnetcore.localization/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Localization.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Localization.xml", - "microsoft.aspnetcore.localization.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.localization.nuspec" - ] - }, - "Microsoft.AspNetCore.Localization.Routing/2.1.0": { - "sha512": "LMvKg4A7CCj9KL2hy+UYVDx9scx5VI9QnvOZvuoPTASZ/pALW1b3MpLrQct3oA8i8Pxi4AWczBqYvYscXGLNMw==", - "type": "package", - "path": "microsoft.aspnetcore.localization.routing/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Localization.Routing.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Localization.Routing.xml", - "microsoft.aspnetcore.localization.routing.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.localization.routing.nuspec" - ] - }, - "Microsoft.AspNetCore.MiddlewareAnalysis/2.1.0": { - "sha512": "K+JQyvfSqSowD343dqFvh1VTgRMtZWlq7Wew+BlECtzoYnUD/24g/SjwpJTaduFOpzmVvhrNqu4dhWYsEIw54Q==", - "type": "package", - "path": "microsoft.aspnetcore.middlewareanalysis/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.MiddlewareAnalysis.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.MiddlewareAnalysis.xml", - "microsoft.aspnetcore.middlewareanalysis.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.middlewareanalysis.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc/2.2.0": { - "sha512": "0arXKbaO07TK6C4QtcIBYHfAjkaUNdKAToMHYZAA8MSpWInhdYwv5rz3RtT00I1E//r+uzpqlUvTWuQcdvZOlQ==", - "type": "package", - "path": "microsoft.aspnetcore.mvc/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.xml", - "microsoft.aspnetcore.mvc.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.Abstractions/2.2.0": { - "sha512": "mQUX/fra89mPzDH1/gP3jPjWBPMlh/5z84N4UutvXP0WrHeasU2E7tpqbGok9t+Mx/p6S+tyMZjMal6tFRfgYQ==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Abstractions.xml", - "microsoft.aspnetcore.mvc.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.Analyzers/2.2.0": { - "sha512": "oyKy2Ey3z+TW+XRZGYJ0qATGzzHYVfpVat+n/rzhYi9GaqedGVYK7q+KFpzmytEMd+qVEztCTklomjLjidxeXg==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.analyzers/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "analyzers/dotnet/cs/Microsoft.AspNetCore.Mvc.Analyzers.dll", - "microsoft.aspnetcore.mvc.analyzers.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.analyzers.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.ApiExplorer/2.2.0": { - "sha512": "LDHy2PvHRgvnhMlM1lQre1ha3FanDOPsiDaOmCWNLAVTNf6l/TItt8B4PxknhPoVYpuWZf3XE6NbIYoSYjuf5A==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.apiexplorer/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ApiExplorer.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ApiExplorer.xml", - "microsoft.aspnetcore.mvc.apiexplorer.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.apiexplorer.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.Core/2.2.5": { - "sha512": "/8sr8ixIUD57UFwUntha9bOwex7/AkZfdk1f9oNJG1Ek7p/uuKVa7fuHmYZpQOf35Oxrt+2Ku4WPwMSbNxOuWg==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.core/2.2.5", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Core.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Core.xml", - "microsoft.aspnetcore.mvc.core.2.2.5.nupkg.sha512", - "microsoft.aspnetcore.mvc.core.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.Cors/2.2.0": { - "sha512": "RvqSd1chAwYHO8Iyiguajp3F+QJ/Blvq3d2Ltlu1vvJuidQQwyZ3ggTsWwc28kib+O2z1L7aq5xC2zKnF6CQOA==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.cors/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Cors.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Cors.xml", - "microsoft.aspnetcore.mvc.cors.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.cors.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.DataAnnotations/2.2.0": { - "sha512": "eLHD8DEim9pey0wrl5K8ICcHoax+Zc5bRpHPJWaqFOVxZns6OtRR/+E0dRNGhJlnOHUo3pHkeD/nMvIocaKnTA==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.dataannotations/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.DataAnnotations.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.DataAnnotations.xml", - "microsoft.aspnetcore.mvc.dataannotations.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.dataannotations.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.Formatters.Json/2.2.0": { - "sha512": "Kbw0p0+FcaGlPLJimKCrEZS8FCmodnCBFxMwAa2lnTeVS7g9A+HWKYr0AaalRcIxtLRZMOaGiYKALvW4SUSmsQ==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.formatters.json/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Json.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Json.xml", - "microsoft.aspnetcore.mvc.formatters.json.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.formatters.json.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.Formatters.Xml/2.1.0": { - "sha512": "FeOfUV1/cPN1UjKxcTlXs1rdXy7IkqWMgunpBPbgPs95IK5TZkTv4Vq86EuDxQ/gj8QndiQatq23SwM6x8OLng==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.formatters.xml/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Xml.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Formatters.Xml.xml", - "microsoft.aspnetcore.mvc.formatters.xml.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.formatters.xml.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.Localization/2.2.0": { - "sha512": "+pAhi7tMP7ZAegR+gVGZDBZoNVaxRk5oVnx7P0PR/hMSIj5010IYotFzCBa63jZGMUvfdXD7rPTxRwC0JOPANw==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.localization/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Localization.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Localization.xml", - "microsoft.aspnetcore.mvc.localization.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.localization.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.Razor/2.2.0": { - "sha512": "9KbXTN4JdryFIiirtKRoMAosklkCt7vzAOs/2Kzz5dV775TxjQofzs96DBxwzcfVlqzLttZ4EHm6wMtDFPbkEg==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.razor/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.xml", - "microsoft.aspnetcore.mvc.razor.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.razor.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.Razor.Extensions/2.2.0": { - "sha512": "+tA54fcNJxLitSbcCs7m6+0BOxLNdhpSowqnLT6wBVFnH9CeaFFTA0JDlg+gPMFeakoJiG0RnfYwAx8BGHcwag==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.razor.extensions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.props", - "build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.targets", - "lib/net46/Microsoft.AspNetCore.Mvc.Razor.Extensions.dll", - "lib/net46/Microsoft.AspNetCore.Mvc.Razor.Extensions.xml", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.Extensions.xml", - "microsoft.aspnetcore.mvc.razor.extensions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.razor.extensions.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.Razor.ViewCompilation/2.1.0": { - "sha512": "mmvtJMhExx78QMq4f0N6pGUjecJGMW64mGS8pZ1Ui3O01fUu1llvM2qsgYOkjaKOuNM3Z60Kx4PUIxpBFoqGng==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.razor.viewcompilation/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation-x64.exe", - "build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation-x86.exe", - "build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.Tasks.dll", - "build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.dll", - "build/netstandard2.0/Microsoft.AspNetCore.Mvc.Razor.ViewCompilation.targets", - "microsoft.aspnetcore.mvc.razor.viewcompilation.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.razor.viewcompilation.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.RazorPages/2.2.0": { - "sha512": "LB7ds5szI3rVJXNgpxHmOaN0PF6HMHdwATt/GVfN4fEZuS9iWHI5kL+ZFrdooqf/X+ZICYxRP617GjhnGjXM8Q==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.razorpages/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.RazorPages.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.RazorPages.xml", - "microsoft.aspnetcore.mvc.razorpages.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.razorpages.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.TagHelpers/2.2.0": { - "sha512": "tJl+jzZaJ5U6jrfVsX/hwzM4TDr7SAponEyhZfV9dmfxl/mt3wAPeFNn3N6Wwpzcna/8rXEln2qEFDAseM+WoQ==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.taghelpers/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.TagHelpers.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.TagHelpers.xml", - "microsoft.aspnetcore.mvc.taghelpers.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.taghelpers.nuspec" - ] - }, - "Microsoft.AspNetCore.Mvc.ViewFeatures/2.2.0": { - "sha512": "uVHF7SWJFNQsddHWug8QY4DRVrcu1d1Yfczqh3noTbDhgfyaz9PLyRgxLCEaeKyaU5G/DfTa7F4U7O0gvYrIMg==", - "type": "package", - "path": "microsoft.aspnetcore.mvc.viewfeatures/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ViewFeatures.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Mvc.ViewFeatures.xml", - "microsoft.aspnetcore.mvc.viewfeatures.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.mvc.viewfeatures.nuspec" - ] - }, - "Microsoft.AspNetCore.NodeServices/2.1.0": { - "sha512": "9TmvzaIWOcag6RCWdACHXaRq0mepN3H2zq7QnqNCRpfl5pTRuyUtQ1kxtC0SgV9B9mWM+vtayHAmsPJKZ/RyPg==", - "type": "package", - "path": "microsoft.aspnetcore.nodeservices/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.NodeServices.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.NodeServices.xml", - "microsoft.aspnetcore.nodeservices.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.nodeservices.nuspec" - ] - }, - "Microsoft.AspNetCore.Owin/2.1.0": { - "sha512": "uhECo09g4kB6CXiETLrdzAvsp/8y8vwTb+egEqryx8w/l0/4fglyli1O7z610ywzwmWA7Gr/5+zamkpRixziHA==", - "type": "package", - "path": "microsoft.aspnetcore.owin/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Owin.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Owin.xml", - "microsoft.aspnetcore.owin.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.owin.nuspec" - ] - }, - "Microsoft.AspNetCore.Razor/2.2.0": { - "sha512": "CI0mRNA/v41q/18qXzUaM850jwdts3i4+EZXxVTZUSTTVxzODqkGIsBv4tWHfJPucM5G9V+9XnNd1itN3Fh1sA==", - "type": "package", - "path": "microsoft.aspnetcore.razor/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.xml", - "microsoft.aspnetcore.razor.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.razor.nuspec" - ] - }, - "Microsoft.AspNetCore.Razor.Design/2.2.0": { - "sha512": "qslnmDEaFHn7ok+xWsqq4YwdKuVIBUV1PAym3OdfXXmMAyI034d5QWPloGDz/i2K2iK3BcFn4qDoBX+xEBAfAg==", - "type": "package", - "path": "microsoft.aspnetcore.razor.design/2.2.0", - "hasTools": true, - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.CodeGeneration.targets", - "build/netstandard2.0/Microsoft.AspNetCore.Razor.Design.props", - "buildMultiTargeting/Microsoft.AspNetCore.Razor.Design.props", - "microsoft.aspnetcore.razor.design.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.razor.design.nuspec", - "tools/Microsoft.AspNetCore.Razor.Language.dll", - "tools/Microsoft.CodeAnalysis.CSharp.dll", - "tools/Microsoft.CodeAnalysis.Razor.dll", - "tools/Microsoft.CodeAnalysis.dll", - "tools/Newtonsoft.Json.dll", - "tools/runtimes/unix/lib/netstandard1.3/System.Text.Encoding.CodePages.dll", - "tools/runtimes/win/lib/netstandard1.3/System.Text.Encoding.CodePages.dll", - "tools/rzc.deps.json", - "tools/rzc.dll", - "tools/rzc.runtimeconfig.json" - ] - }, - "Microsoft.AspNetCore.Razor.Language/2.2.0": { - "sha512": "lyW7L6NFJRSrsRRtfnZu7wzuBWZLroyR8EYRuXnIfjcnhR9EdyEq9rbzguasy/j5fQKbQOfiEDvUsMnDM+ICCA==", - "type": "package", - "path": "microsoft.aspnetcore.razor.language/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net46/Microsoft.AspNetCore.Razor.Language.dll", - "lib/net46/Microsoft.AspNetCore.Razor.Language.xml", - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Language.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Language.xml", - "microsoft.aspnetcore.razor.language.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.razor.language.nuspec" - ] - }, - "Microsoft.AspNetCore.Razor.Runtime/2.2.0": { - "sha512": "W3lGG6PDd0m4UFNplG/fVdcLt2SYKhp6WCstIsb6NmSa6Z2F2TZEXDdf4aU2jWiTmpD1BUvzLNg4U6hovkjvbA==", - "type": "package", - "path": "microsoft.aspnetcore.razor.runtime/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Runtime.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Razor.Runtime.xml", - "microsoft.aspnetcore.razor.runtime.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.razor.runtime.nuspec" - ] - }, - "Microsoft.AspNetCore.ResponseCaching/2.1.0": { - "sha512": "1msF5cXEcT0azHKVhA5xQiZiNivH6bC9u+GMxnqm6RkkOt0wzZnPYcBsRXgGphU7qck6pqtU7gQD9Watez8GIg==", - "type": "package", - "path": "microsoft.aspnetcore.responsecaching/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.xml", - "microsoft.aspnetcore.responsecaching.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.responsecaching.nuspec" - ] - }, - "Microsoft.AspNetCore.ResponseCaching.Abstractions/2.2.0": { - "sha512": "yWA3CAxpCUkXqiHgcg3zTGOrWxpZysaFPu8AlLVpvNp7LmHskvu9y2W8dGOFHNh+D+K1esAir0GSjG0OKJ8VyA==", - "type": "package", - "path": "microsoft.aspnetcore.responsecaching.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCaching.Abstractions.xml", - "microsoft.aspnetcore.responsecaching.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.responsecaching.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.ResponseCompression/2.1.0": { - "sha512": "fQH8EQwoOmL+HGzkxoL5sMsaN8V8AugiBgvBTL0oxcQq1u8iyDo0BmE4qv3VfOkXEqz7GiZQ18RKLa48gWCa5g==", - "type": "package", - "path": "microsoft.aspnetcore.responsecompression/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net461/Microsoft.AspNetCore.ResponseCompression.dll", - "lib/net461/Microsoft.AspNetCore.ResponseCompression.xml", - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCompression.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.ResponseCompression.xml", - "microsoft.aspnetcore.responsecompression.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.responsecompression.nuspec" - ] - }, - "Microsoft.AspNetCore.Rewrite/2.1.0": { - "sha512": "NL80OxLDNzfnlVGEGl013K4X6BHuT4QZ+uLy+4BIvhoO3GwwZP5MpQMj6pbNfu+v4a59yMMQKKQf6fVIXxgN3Q==", - "type": "package", - "path": "microsoft.aspnetcore.rewrite/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Rewrite.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Rewrite.xml", - "microsoft.aspnetcore.rewrite.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.rewrite.nuspec" - ] - }, - "Microsoft.AspNetCore.Routing/2.2.0": { - "sha512": "IiAf1Z/nWas9HX4qd6Wr6UCW95xN7hvCMNwO+1OHNqDQIxgerkM9RLPxWrfE8B6oGz1Z63gE+KT5zuTIfOTv4Q==", - "type": "package", - "path": "microsoft.aspnetcore.routing/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netcoreapp2.2/Microsoft.AspNetCore.Routing.dll", - "lib/netcoreapp2.2/Microsoft.AspNetCore.Routing.xml", - "lib/netstandard2.0/Microsoft.AspNetCore.Routing.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Routing.xml", - "microsoft.aspnetcore.routing.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.routing.nuspec" - ] - }, - "Microsoft.AspNetCore.Routing.Abstractions/2.2.0": { - "sha512": "CKY4qO1chpkysMwX+x436qne91KV25y8uESiW6qpdqjf/tCBOW2WfxJDHEwIpCEZWyVli+n/+7163k6IBESlYA==", - "type": "package", - "path": "microsoft.aspnetcore.routing.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Routing.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Routing.Abstractions.xml", - "microsoft.aspnetcore.routing.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.routing.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Server.HttpSys/2.1.0": { - "sha512": "JfYcbwgvnuzoaQw5AXc2ACehbExht4nGKTCcUHIMr0kAC+Ewc3DYNnSVwvfe0WIoHmsoPgP7ZUlK3WKLQKag9w==", - "type": "package", - "path": "microsoft.aspnetcore.server.httpsys/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.HttpSys.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.HttpSys.xml", - "microsoft.aspnetcore.server.httpsys.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.server.httpsys.nuspec" - ] - }, - "Microsoft.AspNetCore.Server.IIS/2.2.0": { - "sha512": "C64q5Dh2BPAnlbKK9Ma566s0+lcBuQP34EaNP4RkalbikyvetFV5XYHPPwLcCerH/d2k1nPEWVCN8AorXzeahg==", - "type": "package", - "path": "microsoft.aspnetcore.server.iis/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/netstandard2.0/Microsoft.AspNetCore.Server.IIS.targets", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.IIS.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.IIS.xml", - "microsoft.aspnetcore.server.iis.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.server.iis.nuspec", - "runtimes/win-x64/nativeassets/netcoreapp2.2/aspnetcorev2_inprocess.dll", - "runtimes/win-x86/nativeassets/netcoreapp2.2/aspnetcorev2_inprocess.dll" - ] - }, - "Microsoft.AspNetCore.Server.IISIntegration/2.2.0": { - "sha512": "rGFC47Lppb7s4U0iceSymdvFySL+XxwL6pA0yALyFgCWFkSqayk+tAjHU8N+6hbHPSH1YcOCIFcx2yEvL1xb9g==", - "type": "package", - "path": "microsoft.aspnetcore.server.iisintegration/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/netstandard2.0/Microsoft.AspNetCore.Server.IISIntegration.targets", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.IISIntegration.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.IISIntegration.xml", - "microsoft.aspnetcore.server.iisintegration.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.server.iisintegration.nuspec" - ] - }, - "Microsoft.AspNetCore.Server.Kestrel/2.2.0": { - "sha512": "zs+XTC0u0eStJeLmQV/QsRkA2VKXgdgxC5qaQ3J8j0MjUQwYwUnIIUffSnQ+taWs9LK39ngEgQEnsYibkFIlTA==", - "type": "package", - "path": "microsoft.aspnetcore.server.kestrel/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.xml", - "microsoft.aspnetcore.server.kestrel.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.server.kestrel.nuspec" - ] - }, - "Microsoft.AspNetCore.Server.Kestrel.Core/2.2.0": { - "sha512": "W02UcgugxY+CRXnZRdHpq3c00HRGWgxRq1Fk0po6L9taJLRl7BKhI1mTMRoSz0b07LXy2ajHRL7SWBuwg50faw==", - "type": "package", - "path": "microsoft.aspnetcore.server.kestrel.core/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Core.dll", - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Core.xml", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Core.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Core.xml", - "microsoft.aspnetcore.server.kestrel.core.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.server.kestrel.core.nuspec" - ] - }, - "Microsoft.AspNetCore.Server.Kestrel.Https/2.2.0": { - "sha512": "KniGLjOx38+K5DvhX+eh2I8+pOM0R7ZjTRALDSitSsYcmlIYkAdtSEJtEH5CAHuCtT9L58YP3r5sirRpec9r/w==", - "type": "package", - "path": "microsoft.aspnetcore.server.kestrel.https/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Https.dll", - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Https.xml", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Https.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Https.xml", - "microsoft.aspnetcore.server.kestrel.https.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.server.kestrel.https.nuspec" - ] - }, - "Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions/2.2.0": { - "sha512": "WOGP4le2RHaAQwgXqsgbl3fOppmEUVP9LjTLnVSdLjr46ud9JPpd8fuAFR5DKq1RUBJ8aEfms0yZuR7m0WTCSw==", - "type": "package", - "path": "microsoft.aspnetcore.server.kestrel.transport.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Abstractions.xml", - "microsoft.aspnetcore.server.kestrel.transport.abstractions.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.server.kestrel.transport.abstractions.nuspec" - ] - }, - "Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets/2.2.0": { - "sha512": "PwG1DMmg6QJI+cHXJGjNG+HPBerMnolOj9gUlVF9Y0G2MfLov5f0nMSy3iYiWgQT1tgFAmQ3reVOsfwztH/ZLw==", - "type": "package", - "path": "microsoft.aspnetcore.server.kestrel.transport.sockets/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.dll", - "lib/netcoreapp2.1/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.xml", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Server.Kestrel.Transport.Sockets.xml", - "microsoft.aspnetcore.server.kestrel.transport.sockets.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.server.kestrel.transport.sockets.nuspec" - ] - }, - "Microsoft.AspNetCore.Session/2.1.0": { - "sha512": "TpHwtuWVMby2oHWQeQmf6gR/25wqsglTVtGrcDatScXbtB0okZ+LN5J1o9A+mNLGEsRKfrOQqW1JjZ4gb+XTzQ==", - "type": "package", - "path": "microsoft.aspnetcore.session/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.Session.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.Session.xml", - "microsoft.aspnetcore.session.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.session.nuspec" - ] - }, - "Microsoft.AspNetCore.SignalR/1.0.0": { - "sha512": "b6dTJ0HlMSNGGqqMkehFXdkYb0NsQ552ANnc81bwKcBZi2TH0wxW2QZ1/dlYn23w+VbaiY/YrUszkrvPRT68eA==", - "type": "package", - "path": "microsoft.aspnetcore.signalr/1.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.xml", - "microsoft.aspnetcore.signalr.1.0.0.nupkg.sha512", - "microsoft.aspnetcore.signalr.nuspec" - ] - }, - "Microsoft.AspNetCore.SignalR.Common/1.0.0": { - "sha512": "yNmt4OvXIyCZ7HWxjot3IAHZHFwptMBGLh34nPScSyfLuNeci+pMBJhKud2KvrSJrhoJ4crvbYEZ3U4tUjvvgw==", - "type": "package", - "path": "microsoft.aspnetcore.signalr.common/1.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netcoreapp2.1/Microsoft.AspNetCore.SignalR.Common.dll", - "lib/netcoreapp2.1/Microsoft.AspNetCore.SignalR.Common.xml", - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Common.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Common.xml", - "microsoft.aspnetcore.signalr.common.1.0.0.nupkg.sha512", - "microsoft.aspnetcore.signalr.common.nuspec" - ] - }, - "Microsoft.AspNetCore.SignalR.Core/1.0.0": { - "sha512": "GHzZpYkIF4j+Gh8HJ7zunwhZTLMYFhmyo/k2tt2t3NHNMp2V46jeEl738VadQiwAKFApdOQNMKgRNWmbBeePCg==", - "type": "package", - "path": "microsoft.aspnetcore.signalr.core/1.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Core.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Core.xml", - "microsoft.aspnetcore.signalr.core.1.0.0.nupkg.sha512", - "microsoft.aspnetcore.signalr.core.nuspec" - ] - }, - "Microsoft.AspNetCore.SignalR.Protocols.Json/1.0.0": { - "sha512": "kN/lnKkgMkTlLwxYiTgjHwoXENmBoyxN4fLOrDjxgyaR0WJtclPsLKEow91BjpIe9IYDxdv3EspDypNHFN7Zmw==", - "type": "package", - "path": "microsoft.aspnetcore.signalr.protocols.json/1.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Protocols.Json.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.SignalR.Protocols.Json.xml", - "microsoft.aspnetcore.signalr.protocols.json.1.0.0.nupkg.sha512", - "microsoft.aspnetcore.signalr.protocols.json.nuspec" - ] - }, - "Microsoft.AspNetCore.SpaServices/2.1.0": { - "sha512": "7WcqMsogicrxV2a1B/0yepOomtxn2l9yMDxdHOMukIskZ0R5TzW1lT+tw8UgQtKxoAwlnBJFOptl6pa7EAxqfg==", - "type": "package", - "path": "microsoft.aspnetcore.spaservices/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.SpaServices.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.SpaServices.xml", - "microsoft.aspnetcore.spaservices.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.spaservices.nuspec" - ] - }, - "Microsoft.AspNetCore.SpaServices.Extensions/2.1.0": { - "sha512": "m1nXMwZ4k2qk3JXzaRLexUh/Jne8GYOc3dEhP6QarApA3WgD5M15trRTGKvle5QxemEGhxTW7p+XLV1aMt/u3g==", - "type": "package", - "path": "microsoft.aspnetcore.spaservices.extensions/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.SpaServices.Extensions.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.SpaServices.Extensions.xml", - "microsoft.aspnetcore.spaservices.extensions.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.spaservices.extensions.nuspec" - ] - }, - "Microsoft.AspNetCore.StaticFiles/2.2.0": { - "sha512": "dXyjDUxCsWR9MKyGSUfazaOg44kthKPWNC8UPk55WXLfgHP4UNu3g4yrClvyhGWA1smV2Vt6kWNtL2sTFtLl7Q==", - "type": "package", - "path": "microsoft.aspnetcore.staticfiles/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.StaticFiles.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.StaticFiles.xml", - "microsoft.aspnetcore.staticfiles.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.staticfiles.nuspec" - ] - }, - "Microsoft.AspNetCore.WebSockets/2.1.0": { - "sha512": "q8lEFKy1QU45QPhCXPV9FjFKeC13q56gbIuHuxCmGeoKvv/9RaSEdGFfp9JSo6QnXPnD4N4KdC4+A8zrY8KAFg==", - "type": "package", - "path": "microsoft.aspnetcore.websockets/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.WebSockets.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.WebSockets.xml", - "microsoft.aspnetcore.websockets.2.1.0.nupkg.sha512", - "microsoft.aspnetcore.websockets.nuspec" - ] - }, - "Microsoft.AspNetCore.WebUtilities/2.2.0": { - "sha512": "8/l+O/3kTBVjNUcxvYcgY/fnLQf01GzG8VmoASdhYYunmKIcCimk+aY6SvV/3tuQBVkZJV+pcHCQ1+XyjEqfyQ==", - "type": "package", - "path": "microsoft.aspnetcore.webutilities/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.dll", - "lib/netstandard2.0/Microsoft.AspNetCore.WebUtilities.xml", - "microsoft.aspnetcore.webutilities.2.2.0.nupkg.sha512", - "microsoft.aspnetcore.webutilities.nuspec" - ] - }, - "Microsoft.CodeAnalysis.Analyzers/1.1.0": { - "sha512": "EtegM+xm0HLJJJZ3+wcSRGKQmRpYexgObRj/7w65e3OEQ/5pCYNwmE7lbrqfbKC75X0RrFnUGjLRN8f5pnNKrA==", - "type": "package", - "path": "microsoft.codeanalysis.analyzers/1.1.0", - "hasTools": true, - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.rtf", - "analyzers/dotnet/cs/Microsoft.CodeAnalysis.Analyzers.dll", - "analyzers/dotnet/cs/Microsoft.CodeAnalysis.CSharp.Analyzers.dll", - "analyzers/dotnet/vb/Microsoft.CodeAnalysis.Analyzers.dll", - "analyzers/dotnet/vb/Microsoft.CodeAnalysis.VisualBasic.Analyzers.dll", - "microsoft.codeanalysis.analyzers.1.1.0.nupkg.sha512", - "microsoft.codeanalysis.analyzers.nuspec", - "tools/install.ps1", - "tools/uninstall.ps1" - ] - }, - "Microsoft.CodeAnalysis.Common/2.8.0": { - "sha512": "rMr3IN+DZq80ifRZnFr59j5KPbbUHaHOalfNBp4eIF0/tGiksHMTcGncHX56f2QvRembYUY3Qm1AHkOmEz2VZQ==", - "type": "package", - "path": "microsoft.codeanalysis.common/2.8.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Microsoft.CodeAnalysis.dll", - "lib/netstandard1.3/Microsoft.CodeAnalysis.pdb", - "lib/netstandard1.3/Microsoft.CodeAnalysis.xml", - "microsoft.codeanalysis.common.2.8.0.nupkg.sha512", - "microsoft.codeanalysis.common.nuspec" - ] - }, - "Microsoft.CodeAnalysis.CSharp/2.8.0": { - "sha512": "lrINy11M50DAE58OYfLPmiDQ0CNpCYF6fnCiF1Q9pzhqRijfceEJ2s7ior5LP0DWP+YtvcYzMTstd2ujXqF4Vg==", - "type": "package", - "path": "microsoft.codeanalysis.csharp/2.8.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Microsoft.CodeAnalysis.CSharp.dll", - "lib/netstandard1.3/Microsoft.CodeAnalysis.CSharp.pdb", - "lib/netstandard1.3/Microsoft.CodeAnalysis.CSharp.xml", - "microsoft.codeanalysis.csharp.2.8.0.nupkg.sha512", - "microsoft.codeanalysis.csharp.nuspec" - ] - }, - "Microsoft.CodeAnalysis.Razor/2.2.0": { - "sha512": "ZgS8JXCk16LyoKuAiuArba2uAZSwbG+d5pitNecN1SjJqJZhvzW0DP8wX2lLq823Tar3jhxLHMjlSVLowj9c9A==", - "type": "package", - "path": "microsoft.codeanalysis.razor/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net46/Microsoft.CodeAnalysis.Razor.dll", - "lib/net46/Microsoft.CodeAnalysis.Razor.xml", - "lib/netstandard2.0/Microsoft.CodeAnalysis.Razor.dll", - "lib/netstandard2.0/Microsoft.CodeAnalysis.Razor.xml", - "microsoft.codeanalysis.razor.2.2.0.nupkg.sha512", - "microsoft.codeanalysis.razor.nuspec" - ] - }, - "Microsoft.CSharp/4.5.0": { - "sha512": "yWWeTbGCzBOlRPWDCIxiTZW1ecZiMbao0ZT97KKEWdBhrLvUqU8RdzkhzuCRQzvoxzxlR7vytO43OOgFdkxv6g==", - "type": "package", - "path": "microsoft.csharp/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/Microsoft.CSharp.dll", - "lib/netcoreapp2.0/_._", - "lib/netstandard1.3/Microsoft.CSharp.dll", - "lib/netstandard2.0/Microsoft.CSharp.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/uap10.0.16299/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "microsoft.csharp.4.5.0.nupkg.sha512", - "microsoft.csharp.nuspec", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/Microsoft.CSharp.dll", - "ref/netcore50/Microsoft.CSharp.xml", - "ref/netcore50/de/Microsoft.CSharp.xml", - "ref/netcore50/es/Microsoft.CSharp.xml", - "ref/netcore50/fr/Microsoft.CSharp.xml", - "ref/netcore50/it/Microsoft.CSharp.xml", - "ref/netcore50/ja/Microsoft.CSharp.xml", - "ref/netcore50/ko/Microsoft.CSharp.xml", - "ref/netcore50/ru/Microsoft.CSharp.xml", - "ref/netcore50/zh-hans/Microsoft.CSharp.xml", - "ref/netcore50/zh-hant/Microsoft.CSharp.xml", - "ref/netcoreapp2.0/_._", - "ref/netstandard1.0/Microsoft.CSharp.dll", - "ref/netstandard1.0/Microsoft.CSharp.xml", - "ref/netstandard1.0/de/Microsoft.CSharp.xml", - "ref/netstandard1.0/es/Microsoft.CSharp.xml", - "ref/netstandard1.0/fr/Microsoft.CSharp.xml", - "ref/netstandard1.0/it/Microsoft.CSharp.xml", - "ref/netstandard1.0/ja/Microsoft.CSharp.xml", - "ref/netstandard1.0/ko/Microsoft.CSharp.xml", - "ref/netstandard1.0/ru/Microsoft.CSharp.xml", - "ref/netstandard1.0/zh-hans/Microsoft.CSharp.xml", - "ref/netstandard1.0/zh-hant/Microsoft.CSharp.xml", - "ref/netstandard2.0/Microsoft.CSharp.dll", - "ref/netstandard2.0/Microsoft.CSharp.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/uap10.0.16299/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "Microsoft.DotNet.PlatformAbstractions/2.1.0": { - "sha512": "ae/c3pX6vhCs17caUrVZrb8n9gDTzr6usnZKZEHla7uy7ZcjQLfm09gS7m+IneVtmGwXm5hcSDa5qwqwLaH2Ug==", - "type": "package", - "path": "microsoft.dotnet.platformabstractions/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net45/Microsoft.DotNet.PlatformAbstractions.dll", - "lib/netstandard1.3/Microsoft.DotNet.PlatformAbstractions.dll", - "microsoft.dotnet.platformabstractions.2.1.0.nupkg.sha512", - "microsoft.dotnet.platformabstractions.nuspec" - ] - }, - "Microsoft.EntityFrameworkCore/2.1.0": { - "sha512": "wBKrDOJifCm4QBJ9M85RtvfO09llBIY4hWdOkFqZzyBeeMDCkQ/RUolcA9goob6WXmPi2GdzErKIaL6UgCzjGA==", - "type": "package", - "path": "microsoft.entityframeworkcore/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.dll", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.xml", - "microsoft.entityframeworkcore.2.1.0.nupkg.sha512", - "microsoft.entityframeworkcore.nuspec" - ] - }, - "Microsoft.EntityFrameworkCore.Abstractions/2.1.0": { - "sha512": "EX/fD5BFXDqqZfSFS6SBKZY5i8HVrbOqoChCv9UJ2+MLz+RJ4DfkM0UKzf7PtQcOCzwDgUwUfHD+hizohFuLbg==", - "type": "package", - "path": "microsoft.entityframeworkcore.abstractions/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Abstractions.dll", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Abstractions.xml", - "microsoft.entityframeworkcore.abstractions.2.1.0.nupkg.sha512", - "microsoft.entityframeworkcore.abstractions.nuspec" - ] - }, - "Microsoft.EntityFrameworkCore.Analyzers/2.1.0": { - "sha512": "FLhfy+Z9q5+Ye6pifeEciBsxiOjc+cWJUl1yaUwBVBUH8+Q86h9BTNGQbP8jdKZ8q0fcTmt8i6QKdzUN6nVGPg==", - "type": "package", - "path": "microsoft.entityframeworkcore.analyzers/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "analyzers/dotnet/cs/Microsoft.EntityFrameworkCore.Analyzers.dll", - "microsoft.entityframeworkcore.analyzers.2.1.0.nupkg.sha512", - "microsoft.entityframeworkcore.analyzers.nuspec" - ] - }, - "Microsoft.EntityFrameworkCore.Design/2.1.0": { - "sha512": "mfh3QSLoputHW+SQn8WMKdiW25YivELsnK9+n3IK1hJhsOHEK189oD697wiMpGwa5AESZRGc66gDxDh/ljo+HA==", - "type": "package", - "path": "microsoft.entityframeworkcore.design/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/net461/Microsoft.EntityFrameworkCore.Design.props", - "build/netcoreapp2.0/Microsoft.EntityFrameworkCore.Design.props", - "lib/net461/Microsoft.EntityFrameworkCore.Design.dll", - "lib/net461/Microsoft.EntityFrameworkCore.Design.xml", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Design.dll", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Design.xml", - "microsoft.entityframeworkcore.design.2.1.0.nupkg.sha512", - "microsoft.entityframeworkcore.design.nuspec" - ] - }, - "Microsoft.EntityFrameworkCore.InMemory/2.1.0": { - "sha512": "026RycknM8gMrCFRbyRpj/cqb8WEI8ApWWZIZ0IG3tmQ5xJJyAE7PyKhORGnZE+w+3R9DMCV+hDW77+5CbyWsg==", - "type": "package", - "path": "microsoft.entityframeworkcore.inmemory/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.InMemory.dll", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.InMemory.xml", - "microsoft.entityframeworkcore.inmemory.2.1.0.nupkg.sha512", - "microsoft.entityframeworkcore.inmemory.nuspec" - ] - }, - "Microsoft.EntityFrameworkCore.Relational/2.1.0": { - "sha512": "Rpd7CrA1SvmOC1For4IjsXWQATeQb6iJoCq3qnGFVwjssdp9OtyidvU035drWLaibfvAGlu/UfVkbLFLfkaG/Q==", - "type": "package", - "path": "microsoft.entityframeworkcore.relational/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Relational.dll", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.Relational.xml", - "microsoft.entityframeworkcore.relational.2.1.0.nupkg.sha512", - "microsoft.entityframeworkcore.relational.nuspec" - ] - }, - "Microsoft.EntityFrameworkCore.SqlServer/2.1.0": { - "sha512": "YK8rXArw/1a79FgCSKhM5ROqBmOEuKAThjifoOmooDI0VXvkglrq6uF5uhsUQNXWt9qTCi5w88FSKo4/xaIucg==", - "type": "package", - "path": "microsoft.entityframeworkcore.sqlserver/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.SqlServer.dll", - "lib/netstandard2.0/Microsoft.EntityFrameworkCore.SqlServer.xml", - "microsoft.entityframeworkcore.sqlserver.2.1.0.nupkg.sha512", - "microsoft.entityframeworkcore.sqlserver.nuspec" - ] - }, - "Microsoft.EntityFrameworkCore.Tools/2.1.0": { - "sha512": "SR+eLan7CsVgnqA7QB1Y5iZtkN7PTYMVjdtAgmUWsYJg6bWOGslYzvipG3pbCejCc9Y8XLVMHYkbIK3zInLLTQ==", - "type": "package", - "path": "microsoft.entityframeworkcore.tools/2.1.0", - "hasTools": true, - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/_._", - "microsoft.entityframeworkcore.tools.2.1.0.nupkg.sha512", - "microsoft.entityframeworkcore.tools.nuspec", - "tools/EntityFrameworkCore.PowerShell2.psd1", - "tools/EntityFrameworkCore.PowerShell2.psm1", - "tools/EntityFrameworkCore.psd1", - "tools/EntityFrameworkCore.psm1", - "tools/about_EntityFrameworkCore.help.txt", - "tools/init.ps1", - "tools/install.ps1", - "tools/net461/any/ef.exe", - "tools/net461/win-x86/ef.exe", - "tools/netcoreapp2.0/any/ef.dll", - "tools/netcoreapp2.0/any/ef.runtimeconfig.json" - ] - }, - "Microsoft.Extensions.Caching.Abstractions/2.2.0": { - "sha512": "L82fEbPUzi8K6+p4olT8BHtV5SWaIbQg/UGFcsq0STRXJ9I4XR2BazCKJF95Crqn0djENa3qC761eTnOuh+u1w==", - "type": "package", - "path": "microsoft.extensions.caching.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Caching.Abstractions.xml", - "microsoft.extensions.caching.abstractions.2.2.0.nupkg.sha512", - "microsoft.extensions.caching.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.Caching.Memory/2.2.0": { - "sha512": "p0IHvn/uCWZNavHf9EqT1kmZByzSbeqBXbn7Yo13RLIZhCVUsdik4v4gMEgAC+nN0zaTuBwLRysAB1+MFmIUng==", - "type": "package", - "path": "microsoft.extensions.caching.memory/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.dll", - "lib/netstandard2.0/Microsoft.Extensions.Caching.Memory.xml", - "microsoft.extensions.caching.memory.2.2.0.nupkg.sha512", - "microsoft.extensions.caching.memory.nuspec" - ] - }, - "Microsoft.Extensions.Caching.SqlServer/2.1.0": { - "sha512": "IF2qRtJMoLUjhWpTMrsbYuYUM9kJXpAdr91G7LXOguEPMhz0gxyniR/EVDNMEmsPqKHwQzcQpDAmKgrKFUmcQQ==", - "type": "package", - "path": "microsoft.extensions.caching.sqlserver/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Caching.SqlServer.dll", - "lib/netstandard2.0/Microsoft.Extensions.Caching.SqlServer.xml", - "microsoft.extensions.caching.sqlserver.2.1.0.nupkg.sha512", - "microsoft.extensions.caching.sqlserver.nuspec" - ] - }, - "Microsoft.Extensions.Configuration/2.2.0": { - "sha512": "0MtJgCZ5tIw7LoxOlXlifwVTwfMd4YdMJ9+DVZyX/fqqXO9zseZ8hH2WxbJzDbBDp45nA8rwHlL4JH/6Z98B4w==", - "type": "package", - "path": "microsoft.extensions.configuration/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.xml", - "microsoft.extensions.configuration.2.2.0.nupkg.sha512", - "microsoft.extensions.configuration.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.Abstractions/2.2.0": { - "sha512": "M6u4cMWXxPtqyJJ03oezyqTORmSgTPpa2gZRKkEGCXXHhyGnM1cHjPTzq5sevYS1VJEMb2TZj60mAc+hDoPPcQ==", - "type": "package", - "path": "microsoft.extensions.configuration.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Abstractions.xml", - "microsoft.extensions.configuration.abstractions.2.2.0.nupkg.sha512", - "microsoft.extensions.configuration.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.Binder/2.2.0": { - "sha512": "6ANwTuQZqIIt0guKAdS9Mn6c+PfhOBpqRm7FslUJqtG000uo0Cd3pAndk92RQq0ygDFRub/ftiRy8DukWXqNzQ==", - "type": "package", - "path": "microsoft.extensions.configuration.binder/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Binder.xml", - "microsoft.extensions.configuration.binder.2.2.0.nupkg.sha512", - "microsoft.extensions.configuration.binder.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.CommandLine/2.2.0": { - "sha512": "QtNN9TQNAutPxa+bMN58JJZjWZ5njdHJM9tUnwTF6JfPuzlWFZoFRqE029vWwrcq3uw6Tw4/cXjVC7FGOM247A==", - "type": "package", - "path": "microsoft.extensions.configuration.commandline/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.CommandLine.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.CommandLine.xml", - "microsoft.extensions.configuration.commandline.2.2.0.nupkg.sha512", - "microsoft.extensions.configuration.commandline.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.EnvironmentVariables/2.2.0": { - "sha512": "ZjqIn6gtKmAXJa5pWh9n+foC1kDR148LhnnVgbxQcyRC+YR1aWEuiUh2Z3FvSXir/JlnAL+cwqA54dCZVo/LrQ==", - "type": "package", - "path": "microsoft.extensions.configuration.environmentvariables/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.EnvironmentVariables.xml", - "microsoft.extensions.configuration.environmentvariables.2.2.0.nupkg.sha512", - "microsoft.extensions.configuration.environmentvariables.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.FileExtensions/2.2.0": { - "sha512": "BnkmTopJ7m+p0/EBshdJmoWroUvMlu+oA+bDvzEWh3WA9rOYdnTZueQ7Ap1oSfUnHnPBnuWae/ED8hHlM/sQxA==", - "type": "package", - "path": "microsoft.extensions.configuration.fileextensions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.FileExtensions.xml", - "microsoft.extensions.configuration.fileextensions.2.2.0.nupkg.sha512", - "microsoft.extensions.configuration.fileextensions.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.Ini/2.1.0": { - "sha512": "4nr/zv91fWQKYCm/5a5hGnULnPKN1WjsnOSIwtVf2VFM4T0e4/Ec9JG0BJ/Al+o38fiCmpR5brN4zuGEYnGxRg==", - "type": "package", - "path": "microsoft.extensions.configuration.ini/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Ini.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Ini.xml", - "microsoft.extensions.configuration.ini.2.1.0.nupkg.sha512", - "microsoft.extensions.configuration.ini.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.Json/2.2.0": { - "sha512": "CqqFzpFuW9rOWOHI9c3JrGrOOogfLRrGTWVU2AIFkxXRQ/bAg5lL3WabkVEywmTRo58AI3p6sT0ACNy9s279VA==", - "type": "package", - "path": "microsoft.extensions.configuration.json/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Json.xml", - "microsoft.extensions.configuration.json.2.2.0.nupkg.sha512", - "microsoft.extensions.configuration.json.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.KeyPerFile/2.1.0": { - "sha512": "OklDzyGdzS48aFN2qCJ8D9CcHdDaEyHmlumQkGtIjVzLS/MNGleYw7qG7OzSq+ePi29dVbZDrcMJ4BJIwXD6gQ==", - "type": "package", - "path": "microsoft.extensions.configuration.keyperfile/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.KeyPerFile.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.KeyPerFile.xml", - "microsoft.extensions.configuration.keyperfile.2.1.0.nupkg.sha512", - "microsoft.extensions.configuration.keyperfile.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.UserSecrets/2.2.0": { - "sha512": "c9QO9KPMxeGzzPJShGxBi/b7ZxMp/EePdcuq1Rk4kVIR0/tgAmp1Is1a1R/5GASVWBwXAV0xrSiGpE+bNBAF1w==", - "type": "package", - "path": "microsoft.extensions.configuration.usersecrets/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.props", - "build/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.targets", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.UserSecrets.xml", - "microsoft.extensions.configuration.usersecrets.2.2.0.nupkg.sha512", - "microsoft.extensions.configuration.usersecrets.nuspec" - ] - }, - "Microsoft.Extensions.Configuration.Xml/2.1.0": { - "sha512": "hIh+aK2eQCcrLXqKzUDxPTYwPXKZZF2sCfyUaSqpCmtQkDiKUnPfPJaFJx546OKaNkqU7tQUT8+cY3Q7mRKU8A==", - "type": "package", - "path": "microsoft.extensions.configuration.xml/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Xml.dll", - "lib/netstandard2.0/Microsoft.Extensions.Configuration.Xml.xml", - "microsoft.extensions.configuration.xml.2.1.0.nupkg.sha512", - "microsoft.extensions.configuration.xml.nuspec" - ] - }, - "Microsoft.Extensions.DependencyInjection/2.2.0": { - "sha512": "ASF77AJjnyi9hL7IJU1KCAvnCTgI3JEwkU+D4gnKd53nFIYpibVjR6SW8tdTkkuZ+QkmIx2rPvKdTMNVPfVU9A==", - "type": "package", - "path": "microsoft.extensions.dependencyinjection/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net461/Microsoft.Extensions.DependencyInjection.dll", - "lib/net461/Microsoft.Extensions.DependencyInjection.xml", - "lib/netcoreapp2.0/Microsoft.Extensions.DependencyInjection.dll", - "lib/netcoreapp2.0/Microsoft.Extensions.DependencyInjection.xml", - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.dll", - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.xml", - "microsoft.extensions.dependencyinjection.2.2.0.nupkg.sha512", - "microsoft.extensions.dependencyinjection.nuspec" - ] - }, - "Microsoft.Extensions.DependencyInjection.Abstractions/2.2.0": { - "sha512": "2xMk9LHz1EY+7gVG0lG4qBvkUiVjg8QNPqd2HYmEP5+PL7Ayo96EhBieAhd++Gx4yM+xN8kNqmhZdFMBHeG0HQ==", - "type": "package", - "path": "microsoft.extensions.dependencyinjection.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.DependencyInjection.Abstractions.xml", - "microsoft.extensions.dependencyinjection.abstractions.2.2.0.nupkg.sha512", - "microsoft.extensions.dependencyinjection.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.DependencyModel/2.1.0": { - "sha512": "DALND2qA8s/r4jaYMk/3PGa16Qu03iskAzKG62UQKNIN4AueepuuYGydoWScbjxz/TA9AnDAOXRkRuLMYRhzYw==", - "type": "package", - "path": "microsoft.extensions.dependencymodel/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net451/Microsoft.Extensions.DependencyModel.dll", - "lib/netstandard1.3/Microsoft.Extensions.DependencyModel.dll", - "lib/netstandard1.6/Microsoft.Extensions.DependencyModel.dll", - "microsoft.extensions.dependencymodel.2.1.0.nupkg.sha512", - "microsoft.extensions.dependencymodel.nuspec" - ] - }, - "Microsoft.Extensions.DiagnosticAdapter/2.1.0": { - "sha512": "ZGnQR9g+fSxaAqhb5VyD4XgrTXrxC3PMifIGyjx+RVHqZQvllMeHw/JS8novOzhhGTgrG+MRrgeyr6EOBWihWg==", - "type": "package", - "path": "microsoft.extensions.diagnosticadapter/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net461/Microsoft.Extensions.DiagnosticAdapter.dll", - "lib/net461/Microsoft.Extensions.DiagnosticAdapter.xml", - "lib/netcoreapp2.0/Microsoft.Extensions.DiagnosticAdapter.dll", - "lib/netcoreapp2.0/Microsoft.Extensions.DiagnosticAdapter.xml", - "lib/netstandard2.0/Microsoft.Extensions.DiagnosticAdapter.dll", - "lib/netstandard2.0/Microsoft.Extensions.DiagnosticAdapter.xml", - "microsoft.extensions.diagnosticadapter.2.1.0.nupkg.sha512", - "microsoft.extensions.diagnosticadapter.nuspec" - ] - }, - "Microsoft.Extensions.FileProviders.Abstractions/2.2.0": { - "sha512": "33vTyijzAVEfbuNFts68R7bW02cJMiw21MFgMBk+XL+thcbv335PVD1+DdMSvN6e5DeAR4OmPSRsKIr86Yk4qg==", - "type": "package", - "path": "microsoft.extensions.fileproviders.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Abstractions.xml", - "microsoft.extensions.fileproviders.abstractions.2.2.0.nupkg.sha512", - "microsoft.extensions.fileproviders.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.FileProviders.Composite/2.2.0": { - "sha512": "kk46+E+X2VsvJGYBIFA+F3VemmW8/1NleGcmbCPaW/pkHm3UNr7YqC5PmK5Y6CHZQBkHM8IWmMvrKRiieQX8dg==", - "type": "package", - "path": "microsoft.extensions.fileproviders.composite/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Composite.dll", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Composite.xml", - "microsoft.extensions.fileproviders.composite.2.2.0.nupkg.sha512", - "microsoft.extensions.fileproviders.composite.nuspec" - ] - }, - "Microsoft.Extensions.FileProviders.Embedded/2.1.0": { - "sha512": "MrRuTMZDD+1ug4aXw41gw1g5GLgFSQ2tI0nnUeLyJVcbwNECMOejgNXBGliK9BxJD2K/AGp44Z3NSoP/h1N4/w==", - "type": "package", - "path": "microsoft.extensions.fileproviders.embedded/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.props", - "build/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.targets", - "buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.props", - "buildMultiTargeting/Microsoft.Extensions.FileProviders.Embedded.targets", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.dll", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Embedded.xml", - "microsoft.extensions.fileproviders.embedded.2.1.0.nupkg.sha512", - "microsoft.extensions.fileproviders.embedded.nuspec", - "tasks/net461/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll", - "tasks/netstandard1.5/Microsoft.Extensions.FileProviders.Embedded.Manifest.Task.dll" - ] - }, - "Microsoft.Extensions.FileProviders.Physical/2.2.0": { - "sha512": "Qt6sUa56/XbLxYshrCzwJYhHO9TkVa9Adch1qVqr7PGuLCxRs3K7nGMv3I6msUvAJqiJE33Oexz6KrT4hcUHgA==", - "type": "package", - "path": "microsoft.extensions.fileproviders.physical/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.dll", - "lib/netstandard2.0/Microsoft.Extensions.FileProviders.Physical.xml", - "microsoft.extensions.fileproviders.physical.2.2.0.nupkg.sha512", - "microsoft.extensions.fileproviders.physical.nuspec" - ] - }, - "Microsoft.Extensions.FileSystemGlobbing/2.2.0": { - "sha512": "WGASE48sldx5PHrmxpmW0qFJ0M0mZXhF9IgdBDTSNmnys7Qfw7xTuHs/nmbA1Wo8Y8XFI7K2cJKf9Ql/nBK4Rw==", - "type": "package", - "path": "microsoft.extensions.filesystemglobbing/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.dll", - "lib/netstandard2.0/Microsoft.Extensions.FileSystemGlobbing.xml", - "microsoft.extensions.filesystemglobbing.2.2.0.nupkg.sha512", - "microsoft.extensions.filesystemglobbing.nuspec" - ] - }, - "Microsoft.Extensions.Hosting/2.1.0": { - "sha512": "bpQcOKnD2wKKM4veTMkty8keaGcEAEWpESvEEW3z6n+M8nVvLFyrZh56H0/fZYsprvRceuzN2YHiG/48EG7/Ug==", - "type": "package", - "path": "microsoft.extensions.hosting/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Hosting.dll", - "lib/netstandard2.0/Microsoft.Extensions.Hosting.xml", - "microsoft.extensions.hosting.2.1.0.nupkg.sha512", - "microsoft.extensions.hosting.nuspec" - ] - }, - "Microsoft.Extensions.Hosting.Abstractions/2.2.0": { - "sha512": "Uupv0fAgMVBgx6xKnlm4TRrD+aTBQBXCr1QxDFREK+oQ4wrG2lCEmIeqn0YhS17H8T+bRKXTyFBCB5LrOZelWw==", - "type": "package", - "path": "microsoft.extensions.hosting.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Hosting.Abstractions.xml", - "microsoft.extensions.hosting.abstractions.2.2.0.nupkg.sha512", - "microsoft.extensions.hosting.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.Http/2.1.0": { - "sha512": "z36Na5a/9Hu8OCDoq0G1K8/AcTw6D+lHrjIe5i7yhPHBZw6lK5b72Zbuu+Z6RdnKTH7F8l3HVy13MzgjvJa9uA==", - "type": "package", - "path": "microsoft.extensions.http/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Http.dll", - "lib/netstandard2.0/Microsoft.Extensions.Http.xml", - "microsoft.extensions.http.2.1.0.nupkg.sha512", - "microsoft.extensions.http.nuspec" - ] - }, - "Microsoft.Extensions.Identity.Core/2.1.0": { - "sha512": "DzgpB67qX3WntR0bWZoPtyuvpXLgw95op8StFyrZZfANQMGSWp1ay+ue32gWi30rXNokrIW3Dy8WOJcQctD0/Q==", - "type": "package", - "path": "microsoft.extensions.identity.core/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Identity.Core.dll", - "lib/netstandard2.0/Microsoft.Extensions.Identity.Core.xml", - "microsoft.extensions.identity.core.2.1.0.nupkg.sha512", - "microsoft.extensions.identity.core.nuspec" - ] - }, - "Microsoft.Extensions.Identity.Stores/2.1.0": { - "sha512": "1qxs8xfyE3A8ttZaVxgRj08HlK5ubNYBpxQEUjJFKFxVCI9gIH7siLhU3rtUvyahVGQ9MDSiHbAGTrf/wT6eUw==", - "type": "package", - "path": "microsoft.extensions.identity.stores/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Identity.Stores.dll", - "lib/netstandard2.0/Microsoft.Extensions.Identity.Stores.xml", - "microsoft.extensions.identity.stores.2.1.0.nupkg.sha512", - "microsoft.extensions.identity.stores.nuspec" - ] - }, - "Microsoft.Extensions.Localization/2.2.0": { - "sha512": "BDimQdQSdezy/SVvSYJy/cmjaLyAIW18dx3Kl96Z2zoq4LRlaBewOuM9lpGkMAULtQptkan5+rmEIdGMjDIBpQ==", - "type": "package", - "path": "microsoft.extensions.localization/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Localization.dll", - "lib/netstandard2.0/Microsoft.Extensions.Localization.xml", - "microsoft.extensions.localization.2.2.0.nupkg.sha512", - "microsoft.extensions.localization.nuspec" - ] - }, - "Microsoft.Extensions.Localization.Abstractions/2.2.0": { - "sha512": "RMybKFzUHKTiLGKOeDU1AX543ce5kHfn2CBwPeG1kbLo0YMtaR0nngPyFaL1lHv6TrrKWPCfO6p+45dyfPTGgQ==", - "type": "package", - "path": "microsoft.extensions.localization.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Localization.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Localization.Abstractions.xml", - "microsoft.extensions.localization.abstractions.2.2.0.nupkg.sha512", - "microsoft.extensions.localization.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.Logging/2.2.0": { - "sha512": "wa1Uk8sVuEzHtwZPoDNhQiFDjlDe9t74+Iess26uqhIwn02T79v/GB/pLCJ08MOvuM953LfleJW6VPr/wVXCJw==", - "type": "package", - "path": "microsoft.extensions.logging/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Logging.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.xml", - "microsoft.extensions.logging.2.2.0.nupkg.sha512", - "microsoft.extensions.logging.nuspec" - ] - }, - "Microsoft.Extensions.Logging.Abstractions/2.2.0": { - "sha512": "1iYT6BAnJZGebfclQXiM/drK+JUztSikkpT3xcASBQW4og877bsXtjqrMM4EI71nf8UVtvHt6O8pMdYDEE8/Lw==", - "type": "package", - "path": "microsoft.extensions.logging.abstractions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Abstractions.xml", - "microsoft.extensions.logging.abstractions.2.2.0.nupkg.sha512", - "microsoft.extensions.logging.abstractions.nuspec" - ] - }, - "Microsoft.Extensions.Logging.Configuration/2.2.0": { - "sha512": "8ueTyO1RqVOGq+3DZAu/WgRl3ml/Kavh+l1f1xgx+2CDa+8WJeZS8SfgIwqVMlfWLzqkWU7bDCDJiH1kMz4vpA==", - "type": "package", - "path": "microsoft.extensions.logging.configuration/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Configuration.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Configuration.xml", - "microsoft.extensions.logging.configuration.2.2.0.nupkg.sha512", - "microsoft.extensions.logging.configuration.nuspec" - ] - }, - "Microsoft.Extensions.Logging.Console/2.2.0": { - "sha512": "6qC9/FleAqL1yo9s+vyLlgjEQQYPQKnPUWzNsffDDF9+RLwc0uGJGRvE4P+zsNUeYbmEcOwgY0zw54QGRE8mOg==", - "type": "package", - "path": "microsoft.extensions.logging.console/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Console.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Console.xml", - "microsoft.extensions.logging.console.2.2.0.nupkg.sha512", - "microsoft.extensions.logging.console.nuspec" - ] - }, - "Microsoft.Extensions.Logging.Debug/2.2.0": { - "sha512": "iaYa9WAr0utxLOzHj5eVc1sBC0ekQLOtD3Dh9VVZ3eNfUnpiCXc4X2ymslx+QdoTpyJwxuY32qcQrLAIGILdZQ==", - "type": "package", - "path": "microsoft.extensions.logging.debug/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Debug.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.Debug.xml", - "microsoft.extensions.logging.debug.2.2.0.nupkg.sha512", - "microsoft.extensions.logging.debug.nuspec" - ] - }, - "Microsoft.Extensions.Logging.EventSource/2.2.0": { - "sha512": "G7FQp6obMUTKBWRV/SFvVzjMhPNhxImbV/ckf/pFjmq/5g8ZmBGSGOggzB+EPgJ8K60lWBNolepqgfEOoqWi1g==", - "type": "package", - "path": "microsoft.extensions.logging.eventsource/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Logging.EventSource.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.EventSource.xml", - "microsoft.extensions.logging.eventsource.2.2.0.nupkg.sha512", - "microsoft.extensions.logging.eventsource.nuspec" - ] - }, - "Microsoft.Extensions.Logging.TraceSource/2.1.0": { - "sha512": "jM/9VNwvWZ7njQ8XN9vaIXr2j3gMRGOPFmQ+HrvsqSYrAfj+QD8lQbpReUGInXKb3VsZTTDptYc6wxLAH6cHOQ==", - "type": "package", - "path": "microsoft.extensions.logging.tracesource/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Logging.TraceSource.dll", - "lib/netstandard2.0/Microsoft.Extensions.Logging.TraceSource.xml", - "microsoft.extensions.logging.tracesource.2.1.0.nupkg.sha512", - "microsoft.extensions.logging.tracesource.nuspec" - ] - }, - "Microsoft.Extensions.ObjectPool/2.2.0": { - "sha512": "yVTBjTKz1LK+Ouiem03EmMNfeOHZ2zAoptKeY0QY3NAId8luWTj/WJqGkqqifDpUpCDqf3m9RJF+Zf7UDnLNkg==", - "type": "package", - "path": "microsoft.extensions.objectpool/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.dll", - "lib/netstandard2.0/Microsoft.Extensions.ObjectPool.xml", - "microsoft.extensions.objectpool.2.2.0.nupkg.sha512", - "microsoft.extensions.objectpool.nuspec" - ] - }, - "Microsoft.Extensions.Options/2.2.0": { - "sha512": "67zpvElD/YPcfRs1Qm58LSntnSm+kYSA+0PA4+0Abo7NEH4RAvUkV54krspsa0+S4yBZ/m39gUOdn3PSzffazQ==", - "type": "package", - "path": "microsoft.extensions.options/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Options.dll", - "lib/netstandard2.0/Microsoft.Extensions.Options.xml", - "microsoft.extensions.options.2.2.0.nupkg.sha512", - "microsoft.extensions.options.nuspec" - ] - }, - "Microsoft.Extensions.Options.ConfigurationExtensions/2.2.0": { - "sha512": "1GicIsQdkLlVr3wgGIVnzUKuNL9V3NYVkMUXr2rQ7xVOlxNYp7mcrnNrU1S1VI6J1+Jskm7LpSQXk8BPIVnUCg==", - "type": "package", - "path": "microsoft.extensions.options.configurationextensions/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.dll", - "lib/netstandard2.0/Microsoft.Extensions.Options.ConfigurationExtensions.xml", - "microsoft.extensions.options.configurationextensions.2.2.0.nupkg.sha512", - "microsoft.extensions.options.configurationextensions.nuspec" - ] - }, - "Microsoft.Extensions.Primitives/2.2.0": { - "sha512": "vpH+o7f8obVx65PiEtBXxTwL5RosK60fNIMy/y8WGE4/r4IvAqQGukOzMhxsp//Accvd7kmukyQTVkqVYdaDyA==", - "type": "package", - "path": "microsoft.extensions.primitives/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.Primitives.dll", - "lib/netstandard2.0/Microsoft.Extensions.Primitives.xml", - "microsoft.extensions.primitives.2.2.0.nupkg.sha512", - "microsoft.extensions.primitives.nuspec" - ] - }, - "Microsoft.Extensions.WebEncoders/2.2.0": { - "sha512": "9IHT11vqERa5e8ctjVk5TWt60nCNhfMKGpuiw/hw/FZ7UJp+si4x2waFM8NPrx9QoNS4vXEx4FajJLpla2EJsQ==", - "type": "package", - "path": "microsoft.extensions.webencoders/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Extensions.WebEncoders.dll", - "lib/netstandard2.0/Microsoft.Extensions.WebEncoders.xml", - "microsoft.extensions.webencoders.2.2.0.nupkg.sha512", - "microsoft.extensions.webencoders.nuspec" - ] - }, - "Microsoft.IdentityModel.Logging/5.2.0": { - "sha512": "D9uipmVVfJoNv1AzzLR4547+MiwrXElN6bVym3UqZm8/n2LUVdmbHUSQYwqC/gOjHK2vEzXP4NTwHnL+F54q5A==", - "type": "package", - "path": "microsoft.identitymodel.logging/5.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Microsoft.IdentityModel.Logging.dll", - "lib/net45/Microsoft.IdentityModel.Logging.xml", - "lib/net451/Microsoft.IdentityModel.Logging.dll", - "lib/net451/Microsoft.IdentityModel.Logging.xml", - "lib/netstandard1.4/Microsoft.IdentityModel.Logging.dll", - "lib/netstandard1.4/Microsoft.IdentityModel.Logging.xml", - "microsoft.identitymodel.logging.5.2.0.nupkg.sha512", - "microsoft.identitymodel.logging.nuspec" - ] - }, - "Microsoft.IdentityModel.Protocols/5.2.0": { - "sha512": "ZvW6FGD9M4JRJnU9GyCnK22vpWX4itIousHD7v9V/XWb6HUmzfKsb7S9QfzOfnNcNr2eK8nLV50S0v1QMR9ERg==", - "type": "package", - "path": "microsoft.identitymodel.protocols/5.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Microsoft.IdentityModel.Protocols.dll", - "lib/net45/Microsoft.IdentityModel.Protocols.xml", - "lib/net451/Microsoft.IdentityModel.Protocols.dll", - "lib/net451/Microsoft.IdentityModel.Protocols.xml", - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.dll", - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.xml", - "microsoft.identitymodel.protocols.5.2.0.nupkg.sha512", - "microsoft.identitymodel.protocols.nuspec" - ] - }, - "Microsoft.IdentityModel.Protocols.OpenIdConnect/5.2.0": { - "sha512": "IbrtvKFSJLGoCTnDEldkWodt/U3x1OduaFuuVxo8RtvCwZkSp/08OtIlVzdLeJG8bCzQs6p7FV6Xh/a52B0jdw==", - "type": "package", - "path": "microsoft.identitymodel.protocols.openidconnect/5.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll", - "lib/net45/Microsoft.IdentityModel.Protocols.OpenIdConnect.xml", - "lib/net451/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll", - "lib/net451/Microsoft.IdentityModel.Protocols.OpenIdConnect.xml", - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.OpenIdConnect.dll", - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.OpenIdConnect.xml", - "microsoft.identitymodel.protocols.openidconnect.5.2.0.nupkg.sha512", - "microsoft.identitymodel.protocols.openidconnect.nuspec" - ] - }, - "Microsoft.IdentityModel.Protocols.WsFederation/5.2.0": { - "sha512": "tZJI6PD4/QPBXfTNy/FlGYzi+ebV3pUikoYjmEFApuxJ+RXZ2yuqTDR/gpF6m8TuR4jmX3y+ILqYTy0bPuJK9w==", - "type": "package", - "path": "microsoft.identitymodel.protocols.wsfederation/5.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Microsoft.IdentityModel.Protocols.WsFederation.dll", - "lib/net45/Microsoft.IdentityModel.Protocols.WsFederation.xml", - "lib/net451/Microsoft.IdentityModel.Protocols.WsFederation.dll", - "lib/net451/Microsoft.IdentityModel.Protocols.WsFederation.xml", - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.WsFederation.dll", - "lib/netstandard1.4/Microsoft.IdentityModel.Protocols.WsFederation.xml", - "microsoft.identitymodel.protocols.wsfederation.5.2.0.nupkg.sha512", - "microsoft.identitymodel.protocols.wsfederation.nuspec" - ] - }, - "Microsoft.IdentityModel.Tokens/5.2.0": { - "sha512": "ptAbeNOZ++Ioq6eGFhSzcC/oCYMkB/XSuWp9jkaQHz8S2Lxm4wrB2yvYNqlYd88+7L56Ywa9WMCgx94heQvtIw==", - "type": "package", - "path": "microsoft.identitymodel.tokens/5.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Microsoft.IdentityModel.Tokens.dll", - "lib/net45/Microsoft.IdentityModel.Tokens.xml", - "lib/net451/Microsoft.IdentityModel.Tokens.dll", - "lib/net451/Microsoft.IdentityModel.Tokens.xml", - "lib/netstandard1.4/Microsoft.IdentityModel.Tokens.dll", - "lib/netstandard1.4/Microsoft.IdentityModel.Tokens.xml", - "microsoft.identitymodel.tokens.5.2.0.nupkg.sha512", - "microsoft.identitymodel.tokens.nuspec" - ] - }, - "Microsoft.IdentityModel.Tokens.Saml/5.2.0": { - "sha512": "3XCXxwPFOG7uJkDx2hD4vv/5UTKWPnDdmh69jRcXUbEgEC8HVdTbViIrQJien+5WefScalMGYIwA4lnLooxRYw==", - "type": "package", - "path": "microsoft.identitymodel.tokens.saml/5.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Microsoft.IdentityModel.Tokens.Saml.dll", - "lib/net45/Microsoft.IdentityModel.Tokens.Saml.xml", - "lib/net451/Microsoft.IdentityModel.Tokens.Saml.dll", - "lib/net451/Microsoft.IdentityModel.Tokens.Saml.xml", - "lib/netstandard1.4/Microsoft.IdentityModel.Tokens.Saml.dll", - "lib/netstandard1.4/Microsoft.IdentityModel.Tokens.Saml.xml", - "microsoft.identitymodel.tokens.saml.5.2.0.nupkg.sha512", - "microsoft.identitymodel.tokens.saml.nuspec" - ] - }, - "Microsoft.IdentityModel.Xml/5.2.0": { - "sha512": "Z1ID4pp3VhPVR1JKDw5KRqO1WchJEFSrEasiaQRnxstzUrbos4sd3Faro8nlIOtZxS1Dxu5v5UksaoNxlW7BYw==", - "type": "package", - "path": "microsoft.identitymodel.xml/5.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Microsoft.IdentityModel.Xml.dll", - "lib/net45/Microsoft.IdentityModel.Xml.xml", - "lib/net451/Microsoft.IdentityModel.Xml.dll", - "lib/net451/Microsoft.IdentityModel.Xml.xml", - "lib/netstandard1.4/Microsoft.IdentityModel.Xml.dll", - "lib/netstandard1.4/Microsoft.IdentityModel.Xml.xml", - "microsoft.identitymodel.xml.5.2.0.nupkg.sha512", - "microsoft.identitymodel.xml.nuspec" - ] - }, - "Microsoft.IO.RecyclableMemoryStream/1.2.2": { - "sha512": "rVilnCM72Wi9oCAh6sMmOCDSy71/UskHLxhk+OXLJp4C6Uby2GOUeL/9fHMaAYCL4iJECXOOFEfhfgYE5u8HKw==", - "type": "package", - "path": "microsoft.io.recyclablememorystream/1.2.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net40/Microsoft.IO.RecyclableMemoryStream.dll", - "lib/net40/Microsoft.IO.RecyclableMemoryStream.pdb", - "lib/net45/Microsoft.IO.RecyclableMemoryStream.dll", - "lib/net45/Microsoft.IO.RecyclableMemoryStream.pdb", - "lib/netstandard1.4/Microsoft.IO.RecyclableMemoryStream.deps.json", - "lib/netstandard1.4/Microsoft.IO.RecyclableMemoryStream.dll", - "lib/netstandard1.4/Microsoft.IO.RecyclableMemoryStream.pdb", - "microsoft.io.recyclablememorystream.1.2.2.nupkg.sha512", - "microsoft.io.recyclablememorystream.nuspec" - ] - }, - "Microsoft.Net.Http.Headers/2.2.0": { - "sha512": "eiimzOKCgMyHUTR6604ZDZDpfc+5MK0VJAEPsBo8yF96ZBXHRS2ERe7SkyffoEN8sz/0NLa0MFngb5DwOwTqZQ==", - "type": "package", - "path": "microsoft.net.http.headers/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Microsoft.Net.Http.Headers.dll", - "lib/netstandard2.0/Microsoft.Net.Http.Headers.xml", - "microsoft.net.http.headers.2.2.0.nupkg.sha512", - "microsoft.net.http.headers.nuspec" - ] - }, - "Microsoft.NETCore.Platforms/2.1.0": { - "sha512": "ok+RPAtESz/9MUXeIEz6Lv5XAGQsaNmEYXMsgVALj4D7kqC8gveKWXWXbufLySR2fWrwZf8smyN5RmHu0e4BHA==", - "type": "package", - "path": "microsoft.netcore.platforms/2.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/_._", - "microsoft.netcore.platforms.2.1.0.nupkg.sha512", - "microsoft.netcore.platforms.nuspec", - "runtime.json", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "Microsoft.NETCore.Targets/1.1.0": { - "sha512": "HvAzpoaIqrmZfsHAN4rLFji0r7YY5TP8r3SdkziXN7qU9KCpRIG+zfcBx+mIri/jkBNqjq4iWIfuYNtFBjSQ/g==", - "type": "package", - "path": "microsoft.netcore.targets/1.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "microsoft.netcore.targets.1.1.0.nupkg.sha512", - "microsoft.netcore.targets.nuspec", - "runtime.json" - ] - }, - "Microsoft.Win32.Primitives/4.3.0": { - "sha512": "Nm8Hp51y9tYcK3xD6qk43Wjftrg1mdH24CCJsTb6gr7HS21U1uA+CKPGEtUcVZbjU1y8Kynzm5eoJ7Pnx5gm8A==", - "type": "package", - "path": "microsoft.win32.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/Microsoft.Win32.Primitives.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "microsoft.win32.primitives.4.3.0.nupkg.sha512", - "microsoft.win32.primitives.nuspec", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/Microsoft.Win32.Primitives.dll", - "ref/netstandard1.3/Microsoft.Win32.Primitives.dll", - "ref/netstandard1.3/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/de/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/es/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/fr/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/it/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/ja/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/ko/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/ru/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/zh-hans/Microsoft.Win32.Primitives.xml", - "ref/netstandard1.3/zh-hant/Microsoft.Win32.Primitives.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._" - ] - }, - "Microsoft.Win32.Registry/4.5.0": { - "sha512": "LKmf0FiggwZBhNoz0S9ypcQ0EhUdQmwwniKEpd9LcisvJtxyYWq2RSok7YFpODm3VoYdR+6ifWDX23/2dJq2ZA==", - "type": "package", - "path": "microsoft.win32.registry/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net46/Microsoft.Win32.Registry.dll", - "lib/net461/Microsoft.Win32.Registry.dll", - "lib/netstandard1.3/Microsoft.Win32.Registry.dll", - "lib/netstandard2.0/Microsoft.Win32.Registry.dll", - "microsoft.win32.registry.4.5.0.nupkg.sha512", - "microsoft.win32.registry.nuspec", - "ref/net46/Microsoft.Win32.Registry.dll", - "ref/net461/Microsoft.Win32.Registry.dll", - "ref/net461/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/Microsoft.Win32.Registry.dll", - "ref/netstandard1.3/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/de/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/es/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/fr/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/it/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/ja/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/ko/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/ru/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/zh-hans/Microsoft.Win32.Registry.xml", - "ref/netstandard1.3/zh-hant/Microsoft.Win32.Registry.xml", - "ref/netstandard2.0/Microsoft.Win32.Registry.dll", - "ref/netstandard2.0/Microsoft.Win32.Registry.xml", - "runtimes/unix/lib/netstandard2.0/Microsoft.Win32.Registry.dll", - "runtimes/win/lib/net46/Microsoft.Win32.Registry.dll", - "runtimes/win/lib/net461/Microsoft.Win32.Registry.dll", - "runtimes/win/lib/netstandard1.3/Microsoft.Win32.Registry.dll", - "runtimes/win/lib/netstandard2.0/Microsoft.Win32.Registry.dll", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "MongoDB.Bson/2.7.0": { - "sha512": "BBPKyeYmhRduXuDQsFvB2bw34rAqJrT9UZPHAVLGLtvp5wYxhzP8r7SrSn//f91Xst09Ww/5lq3DD5byn0fdLw==", - "type": "package", - "path": "mongodb.bson/2.7.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "License.rtf", - "lib/net45/MongoDB.Bson.dll", - "lib/net45/MongoDB.Bson.xml", - "lib/netstandard1.5/MongoDB.Bson.dll", - "lib/netstandard1.5/MongoDB.Bson.xml", - "mongodb.bson.2.7.0.nupkg.sha512", - "mongodb.bson.nuspec" - ] - }, - "Multiformats.Base/2.0.1": { - "sha512": "JherI2cl97crsQHN5pwwNIlz004D64szvvXRRq8XVXQR2ZOFTaW5UEs8sJmt80bhW3cHH7XP4ooCqGYr/WBNRw==", - "type": "package", - "path": "multiformats.base/2.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net461/Multiformats.Base.dll", - "lib/netstandard1.6/Multiformats.Base.dll", - "multiformats.base.2.0.1.nupkg.sha512", - "multiformats.base.nuspec" - ] - }, - "Multiformats.Hash/1.5.0": { - "sha512": "f9HstrBNHUWs0WFhYH7H4H3VatzTVop+XWp0QDFW7f9JzeIj2fnz21P0IrgwR8H6wl1ujAEh+5yf30XlqRDcaQ==", - "type": "package", - "path": "multiformats.hash/1.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net461/Multiformats.Hash.dll", - "lib/net461/Multiformats.Hash.pdb", - "lib/netstandard1.6/Multiformats.Hash.dll", - "lib/netstandard1.6/Multiformats.Hash.pdb", - "lib/netstandard2.0/Multiformats.Hash.dll", - "lib/netstandard2.0/Multiformats.Hash.pdb", - "multiformats.hash.1.5.0.nupkg.sha512", - "multiformats.hash.nuspec" - ] - }, - "murmurhash/1.0.2": { - "sha512": "Yw9+sYL3qdTEXDKAEeiXsVwsP2K2nyWOxgvbDD1w5j+yu0CYk5edLvGmmJHqqFxuBFrVsgb7iF2XGprRlt+SEA==", - "type": "package", - "path": "murmurhash/1.0.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net35/MurmurHash.dll", - "lib/net40/MurmurHash.dll", - "lib/net45/MurmurHash.dll", - "lib/netstandard1.4/MurmurHash.dll", - "murmurhash.1.0.2.nupkg.sha512", - "murmurhash.nuspec" - ] - }, - "Nethereum.Hex/3.3.0": { - "sha512": "N5RP4U901vUvRj74DMAwFm1gxXrrfVgKcObtcPczvY2yczP7gIhMCuYQWIzBzau43UwgUAn1ZjvgsWZjWxdotg==", - "type": "package", - "path": "nethereum.hex/3.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.md", - "lib/net451/Nethereum.Hex.dll", - "lib/netcoreapp2.1/Nethereum.Hex.dll", - "lib/netstandard1.1/Nethereum.Hex.dll", - "lib/netstandard2.0/Nethereum.Hex.dll", - "nethereum.hex.3.3.0.nupkg.sha512", - "nethereum.hex.nuspec" - ] - }, - "Nethereum.RLP/3.3.0": { - "sha512": "hWRhRMvpkkXiuCO5Jo9n+DBV03xRU06KTOh+ZBavC/jdLCnuPdwZbpciZt9BMMtg3z+Bl6UmFGOZxDuZPNi2zg==", - "type": "package", - "path": "nethereum.rlp/3.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.md", - "lib/net451/Nethereum.RLP.dll", - "lib/netcoreapp2.1/Nethereum.RLP.dll", - "lib/netstandard1.1/Nethereum.RLP.dll", - "lib/netstandard2.0/Nethereum.RLP.dll", - "nethereum.rlp.3.3.0.nupkg.sha512", - "nethereum.rlp.nuspec" - ] - }, - "NETStandard.Library/1.6.1": { - "sha512": "CXLcLbtJJeiW9ivOLlnU5IY5Mg7jitMBbc1IX71pNqDtCAc61e7yphLf8F38OQ85MP/5552HoGBw7rgSgnfL0A==", - "type": "package", - "path": "netstandard.library/1.6.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "netstandard.library.1.6.1.nupkg.sha512", - "netstandard.library.nuspec" - ] - }, - "Newtonsoft.Json/12.0.2": { - "sha512": "rTK0s2EKlfHsQsH6Yx2smvcTCeyoDNgCW7FEYyV01drPlh2T243PR2DiDXqtC5N4GDm4Ma/lkxfW5a/4793vbA==", - "type": "package", - "path": "newtonsoft.json/12.0.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.md", - "lib/net20/Newtonsoft.Json.dll", - "lib/net20/Newtonsoft.Json.xml", - "lib/net35/Newtonsoft.Json.dll", - "lib/net35/Newtonsoft.Json.xml", - "lib/net40/Newtonsoft.Json.dll", - "lib/net40/Newtonsoft.Json.xml", - "lib/net45/Newtonsoft.Json.dll", - "lib/net45/Newtonsoft.Json.xml", - "lib/netstandard1.0/Newtonsoft.Json.dll", - "lib/netstandard1.0/Newtonsoft.Json.xml", - "lib/netstandard1.3/Newtonsoft.Json.dll", - "lib/netstandard1.3/Newtonsoft.Json.xml", - "lib/netstandard2.0/Newtonsoft.Json.dll", - "lib/netstandard2.0/Newtonsoft.Json.xml", - "lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.dll", - "lib/portable-net40+sl5+win8+wp8+wpa81/Newtonsoft.Json.xml", - "lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.dll", - "lib/portable-net45+win8+wp8+wpa81/Newtonsoft.Json.xml", - "newtonsoft.json.12.0.2.nupkg.sha512", - "newtonsoft.json.nuspec" - ] - }, - "Newtonsoft.Json.Bson/1.0.1": { - "sha512": "W5Ke5xei9yS0ljQZuT75VgSp5M43eCPm5hHAelvKyGGU4dV7hYCmtwdsxoADb/exO6pYHeu/Iki43TdYPzfESQ==", - "type": "package", - "path": "newtonsoft.json.bson/1.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Newtonsoft.Json.Bson.dll", - "lib/net45/Newtonsoft.Json.Bson.xml", - "lib/netstandard1.3/Newtonsoft.Json.Bson.dll", - "lib/netstandard1.3/Newtonsoft.Json.Bson.xml", - "newtonsoft.json.bson.1.0.1.nupkg.sha512", - "newtonsoft.json.bson.nuspec" - ] - }, - "Nito.AsyncEx/5.0.0": { - "sha512": "EgeMl8BoaLg6mOXs7MVjl3uqUqZlL0uYti2/G128EA60U9UqoYlF0AMtGOA1/EP+bvrjcvixK8D3MLpwr2dnSw==", - "type": "package", - "path": "nito.asyncex/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "nito.asyncex.5.0.0.nupkg.sha512", - "nito.asyncex.nuspec" - ] - }, - "Nito.AsyncEx.Context/5.0.0": { - "sha512": "Qnth1Ye+QSLg8P3fSFYzk7ue6oUUHQcKpLitgAig8xRFqTK5W1KTlfxF/Z8Eo0BuqZ17a5fAGtXrdKJsLqivZw==", - "type": "package", - "path": "nito.asyncex.context/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Nito.AsyncEx.Context.dll", - "lib/netstandard1.3/Nito.AsyncEx.Context.xml", - "lib/netstandard2.0/Nito.AsyncEx.Context.dll", - "lib/netstandard2.0/Nito.AsyncEx.Context.xml", - "nito.asyncex.context.5.0.0.nupkg.sha512", - "nito.asyncex.context.nuspec" - ] - }, - "Nito.AsyncEx.Coordination/5.0.0": { - "sha512": "kjauyO8UMo/FGZO/M8TdjXB8ZlBPFOiRN8yakThaGQbYOywazQ0kGZ39SNr2gNNzsTxbZOUudBMYNo+IrtscbA==", - "type": "package", - "path": "nito.asyncex.coordination/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Nito.AsyncEx.Coordination.dll", - "lib/netstandard1.3/Nito.AsyncEx.Coordination.xml", - "lib/netstandard2.0/Nito.AsyncEx.Coordination.dll", - "lib/netstandard2.0/Nito.AsyncEx.Coordination.xml", - "nito.asyncex.coordination.5.0.0.nupkg.sha512", - "nito.asyncex.coordination.nuspec" - ] - }, - "Nito.AsyncEx.Interop.WaitHandles/5.0.0": { - "sha512": "roX7jQ7AAEw3CUq/oRj/ACcN1XtfO6fSbYSAjMtjvacN9pqY7Uq2reBFaosbMaMt16EcW+Xw3PwczKzHdZFPRA==", - "type": "package", - "path": "nito.asyncex.interop.waithandles/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Nito.AsyncEx.Interop.WaitHandles.dll", - "lib/netstandard1.3/Nito.AsyncEx.Interop.WaitHandles.xml", - "lib/netstandard2.0/Nito.AsyncEx.Interop.WaitHandles.dll", - "lib/netstandard2.0/Nito.AsyncEx.Interop.WaitHandles.xml", - "nito.asyncex.interop.waithandles.5.0.0.nupkg.sha512", - "nito.asyncex.interop.waithandles.nuspec" - ] - }, - "Nito.AsyncEx.Oop/5.0.0": { - "sha512": "SKyHsxzXD8b69tPPgMDh7mgMx7GdaSfJ1h2eNS+z2Z/vHsutXTl1sYLOO30fiIkv1umvI1g8GySvYLpVlpt6yA==", - "type": "package", - "path": "nito.asyncex.oop/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Nito.AsyncEx.Oop.dll", - "lib/netstandard1.3/Nito.AsyncEx.Oop.xml", - "lib/netstandard2.0/Nito.AsyncEx.Oop.dll", - "lib/netstandard2.0/Nito.AsyncEx.Oop.xml", - "nito.asyncex.oop.5.0.0.nupkg.sha512", - "nito.asyncex.oop.nuspec" - ] - }, - "Nito.AsyncEx.Tasks/5.0.0": { - "sha512": "ZtvotignafOLteP4oEjVcF3k2L8h73QUCaFpVKWbU+EOlW/I+JGkpMoXIl0rlwPcDmR84RxzggLRUNMaWlOosA==", - "type": "package", - "path": "nito.asyncex.tasks/5.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.3/Nito.AsyncEx.Tasks.dll", - "lib/netstandard1.3/Nito.AsyncEx.Tasks.xml", - "lib/netstandard2.0/Nito.AsyncEx.Tasks.dll", - "lib/netstandard2.0/Nito.AsyncEx.Tasks.xml", - "nito.asyncex.tasks.5.0.0.nupkg.sha512", - "nito.asyncex.tasks.nuspec" - ] - }, - "Nito.Cancellation/1.0.5": { - "sha512": "/MehN8HvuOgPZsNmqyu0UdPWXCAJVqcy0LEqw1Ch1++ag1h0qPt5YLH+BCIMFDC5UMW/4RzJW2GdRDYg7Vi2yA==", - "type": "package", - "path": "nito.cancellation/1.0.5", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.0/Nito.Cancellation.dll", - "lib/netstandard1.0/Nito.Cancellation.xml", - "lib/netstandard2.0/Nito.Cancellation.dll", - "lib/netstandard2.0/Nito.Cancellation.xml", - "nito.cancellation.1.0.5.nupkg.sha512", - "nito.cancellation.nuspec" - ] - }, - "Nito.Collections.Deque/1.0.4": { - "sha512": "yGDKqCQ61i97MyfEUYG6+ln5vxpx11uA5M9+VV9B7stticbFm19YMI/G9w4AFYVBj5PbPi138P8IovkMFAL0Aw==", - "type": "package", - "path": "nito.collections.deque/1.0.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.0/Nito.Collections.Deque.dll", - "lib/netstandard1.0/Nito.Collections.Deque.xml", - "lib/netstandard2.0/Nito.Collections.Deque.dll", - "lib/netstandard2.0/Nito.Collections.Deque.xml", - "nito.collections.deque.1.0.4.nupkg.sha512", - "nito.collections.deque.nuspec" - ] - }, - "Nito.Disposables/2.0.0": { - "sha512": "ExJl/jTjegSLHGcwnmaYaI5xIlrefAsVdeLft7VLtXI2+W5irihiu36LizWvlaUpzY1/llo+YSh09uSHMu2VFw==", - "type": "package", - "path": "nito.disposables/2.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.0/Nito.Disposables.dll", - "lib/netstandard1.0/Nito.Disposables.pdb", - "lib/netstandard1.0/Nito.Disposables.xml", - "lib/netstandard2.0/Nito.Disposables.dll", - "lib/netstandard2.0/Nito.Disposables.pdb", - "lib/netstandard2.0/Nito.Disposables.xml", - "nito.disposables.2.0.0.nupkg.sha512", - "nito.disposables.nuspec" - ] - }, - "NLog/4.6.5": { - "sha512": "iM0VsNBxnyzyoJWUBrXkFJvHRWTeaVAPjRETzknnGNVicD7C4FYpWszzdF43ugTh6a+StbZffaII9KKEFM5xsA==", - "type": "package", - "path": "nlog/4.6.5", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/monoandroid44/NLog.dll", - "lib/monoandroid44/NLog.xml", - "lib/net35/NLog.dll", - "lib/net35/NLog.xml", - "lib/net40-client/NLog.dll", - "lib/net40-client/NLog.xml", - "lib/net45/NLog.dll", - "lib/net45/NLog.xml", - "lib/netstandard1.3/NLog.dll", - "lib/netstandard1.3/NLog.xml", - "lib/netstandard1.5/NLog.dll", - "lib/netstandard1.5/NLog.xml", - "lib/netstandard2.0/NLog.dll", - "lib/netstandard2.0/NLog.xml", - "lib/sl4/NLog.dll", - "lib/sl4/NLog.xml", - "lib/sl5/NLog.dll", - "lib/sl5/NLog.xml", - "lib/wp8/NLog.dll", - "lib/wp8/NLog.xml", - "lib/xamarinios10/NLog.dll", - "lib/xamarinios10/NLog.xml", - "nlog.4.6.5.nupkg.sha512", - "nlog.nuspec" - ] - }, - "NLog.StructuredLogging.Json/4.0.0": { - "sha512": "REL10hQplUeZozMAeYSbljkMT0PrCMO0G0ILnuWlleqTkl52CD99ILWUq+WyJiPQoxRlzDVYUZkMQQx65ez32g==", - "type": "package", - "path": "nlog.structuredlogging.json/4.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/NLog.StructuredLogging.Json.dll", - "lib/net45/NLog.StructuredLogging.Json.pdb", - "lib/net45/NLog.StructuredLogging.Json.runtimeconfig.json", - "lib/netstandard2.0/NLog.StructuredLogging.Json.dll", - "lib/netstandard2.0/NLog.StructuredLogging.Json.pdb", - "lib/netstandard2.0/NLog.StructuredLogging.Json.runtimeconfig.json", - "nlog.structuredlogging.json.4.0.0.nupkg.sha512", - "nlog.structuredlogging.json.nuspec" - ] - }, - "NLog.Targets.Seq/1.1.0": { - "sha512": "L1M9oX0gWbp0aqZWMRkm7qnGmcvMt4KYonwtnvNfQQ0+dN6gxXufvR3dEOWNMtyofPQFUhz8VzcJ9y1USkXIqg==", - "type": "package", - "path": "nlog.targets.seq/1.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/NLog.Targets.Seq.dll", - "lib/net45/NLog.Targets.Seq.xml", - "lib/netstandard2.0/NLog.Targets.Seq.dll", - "lib/netstandard2.0/NLog.Targets.Seq.xml", - "nlog.targets.seq.1.1.0.nupkg.sha512", - "nlog.targets.seq.nuspec" - ] - }, - "NLog.Targets.Syslog/5.1.0": { - "sha512": "W97RMEDe3dO98rIhX0pd3fdpTu7vsc91uxGi10W3zPAxdD78GUqtGeHyqmjsPXT0B8vtiYPGD26QXao/Jqo5YA==", - "type": "package", - "path": "nlog.targets.syslog/5.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net452/NLog.Targets.Syslog.dll", - "lib/net452/NLog.Targets.Syslog.xml", - "lib/netstandard2.0/NLog.Targets.Syslog.dll", - "lib/netstandard2.0/NLog.Targets.Syslog.xml", - "nlog.targets.syslog.5.1.0.nupkg.sha512", - "nlog.targets.syslog.nuspec" - ] - }, - "PeerTalk/0.11.3": { - "sha512": "FehIZRZZgu3bTz3uQfTTRfKnLi0pZpHu31yPFU4mXzc7jdBxYq5LUiMM/NE8dHwkV00XyAa+dkFqrPGD5ui0Cg==", - "type": "package", - "path": "peertalk/0.11.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net461/PeerTalk.dll", - "lib/net461/PeerTalk.xml", - "lib/netstandard1.4/PeerTalk.dll", - "lib/netstandard1.4/PeerTalk.xml", - "lib/netstandard2.0/PeerTalk.dll", - "lib/netstandard2.0/PeerTalk.xml", - "peertalk.0.11.3.nupkg.sha512", - "peertalk.nuspec" - ] - }, - "PeterO.Cbor/3.1.0": { - "sha512": "J5xQ+7RJAdO9plzuFqkq92rlMXiUkzY9H8FvpuNw/QN2VNChHgoAjw9EhkBeiqJa6HvCmNy/sf73afDNsMXvXQ==", - "type": "package", - "path": "petero.cbor/3.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.0/CBOR.dll", - "lib/netstandard1.0/CBOR.xml", - "petero.cbor.3.1.0.nupkg.sha512", - "petero.cbor.nuspec" - ] - }, - "PeterO.Numbers/1.0.2": { - "sha512": "cQTX4A/w7PQjEbkTRtIz24cj9bkHnULpG6BcI5zxIS7KwdxQPxJZepUx7BBJHP1hSd+yZ0hJYp+wpX7c55zFlg==", - "type": "package", - "path": "petero.numbers/1.0.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.0/Numbers.dll", - "lib/netstandard1.0/Numbers.xml", - "petero.numbers.1.0.2.nupkg.sha512", - "petero.numbers.nuspec" - ] - }, - "Polly/7.1.0": { - "sha512": "mpQsvlEn4ipgICh5CGyVLPV93RFVzOrBG7wPZfzY8gExh7XgWQn0GCDY9nudbUEJ12UiGPP9i4+CohghBvzhmg==", - "type": "package", - "path": "polly/7.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard1.1/Polly.dll", - "lib/netstandard1.1/Polly.xml", - "lib/netstandard2.0/Polly.dll", - "lib/netstandard2.0/Polly.xml", - "polly.7.1.0.nupkg.sha512", - "polly.nuspec" - ] - }, - "Portable.BouncyCastle/1.8.5": { - "sha512": "EaCgmntbH1sOzemRTqyXSqYjB6pLH7VCYHhhDYZ59guHSD5qPwhIYa7kfy0QUlmTRt9IXhaXdFhNuBUArp70Ng==", - "type": "package", - "path": "portable.bouncycastle/1.8.5", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net40/BouncyCastle.Crypto.dll", - "lib/net40/BouncyCastle.Crypto.xml", - "lib/netstandard1.0/BouncyCastle.Crypto.dll", - "lib/netstandard1.0/BouncyCastle.Crypto.xml", - "lib/netstandard1.3/BouncyCastle.Crypto.dll", - "lib/netstandard1.3/BouncyCastle.Crypto.xml", - "lib/netstandard2.0/BouncyCastle.Crypto.dll", - "lib/netstandard2.0/BouncyCastle.Crypto.xml", - "portable.bouncycastle.1.8.5.nupkg.sha512", - "portable.bouncycastle.nuspec" - ] - }, - "protobuf-net/2.4.0": { - "sha512": "j37MD1p1s9NdX8P5+IaY2J9p2382xiL1VP3mxYu0g+G/kf2YM2grFa1jJPO+0WDJNl1XhNPO0Q5yBEcbX77hBQ==", - "type": "package", - "path": "protobuf-net/2.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net20/protobuf-net.dll", - "lib/net20/protobuf-net.xml", - "lib/net35/protobuf-net.dll", - "lib/net35/protobuf-net.xml", - "lib/net40/protobuf-net.dll", - "lib/net40/protobuf-net.xml", - "lib/netcoreapp2.1/protobuf-net.dll", - "lib/netcoreapp2.1/protobuf-net.xml", - "lib/netstandard1.0/protobuf-net.dll", - "lib/netstandard1.0/protobuf-net.xml", - "lib/netstandard1.3/protobuf-net.dll", - "lib/netstandard1.3/protobuf-net.xml", - "lib/netstandard2.0/protobuf-net.dll", - "lib/netstandard2.0/protobuf-net.xml", - "lib/uap10.0/protobuf-net.dll", - "lib/uap10.0/protobuf-net.pri", - "lib/uap10.0/protobuf-net.xml", - "protobuf-net.2.4.0.nupkg.sha512", - "protobuf-net.nuspec" - ] - }, - "Remotion.Linq/2.2.0": { - "sha512": "twDAH8dAXXCAf3sRv1Tf94u66eEHvgU75hfn1nn2v4fSWXD50XoDOAk8WpSrbViNuMkB4kN1ElnOGm1c519IHg==", - "type": "package", - "path": "remotion.linq/2.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net35/Remotion.Linq.XML", - "lib/net35/Remotion.Linq.dll", - "lib/net40/Remotion.Linq.XML", - "lib/net40/Remotion.Linq.dll", - "lib/net45/Remotion.Linq.XML", - "lib/net45/Remotion.Linq.dll", - "lib/netstandard1.0/Remotion.Linq.dll", - "lib/netstandard1.0/Remotion.Linq.xml", - "lib/portable-net45+win+wpa81+wp80/Remotion.Linq.dll", - "lib/portable-net45+win+wpa81+wp80/Remotion.Linq.xml", - "remotion.linq.2.2.0.nupkg.sha512", - "remotion.linq.nuspec" - ] - }, - "runtime.debian.8-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "7VSGO0URRKoMEAq0Sc9cRz8mb6zbyx/BZDEWhgPdzzpmFhkam3fJ1DAGWFXBI4nGlma+uPKpfuMQP5LXRnOH5g==", - "type": "package", - "path": "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.debian.8-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/debian.8-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.fedora.23-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "0oAaTAm6e2oVH+/Zttt0cuhGaePQYKII1dY8iaqP7CvOpVKgLybKRFvQjXR2LtxXOXTVPNv14j0ot8uV+HrUmw==", - "type": "package", - "path": "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.fedora.23-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/fedora.23-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.fedora.24-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "G24ibsCNi5Kbz0oXWynBoRgtGvsw5ZSVEWjv13/KiCAM8C6wz9zzcCniMeQFIkJ2tasjo2kXlvlBZhplL51kGg==", - "type": "package", - "path": "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.fedora.24-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/fedora.24-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.native.System/4.3.0": { - "sha512": "KZxalv/9yvGXLj49HHJ4N9GKyeiMt5wJkU8S/x3nKA3/EMkjKkmhwdO6d4Wlz3byjJ3OQU8KKlZ2iN5/1TMdyA==", - "type": "package", - "path": "runtime.native.system/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.4.3.0.nupkg.sha512", - "runtime.native.system.nuspec" - ] - }, - "runtime.native.System.Data.SqlClient.sni/4.4.0": { - "sha512": "A8v6PGmk+UGbfWo5Ixup0lPM4swuSwOiayJExZwKIOjTlFFQIsu3QnDXECosBEyrWSPryxBVrdqtJyhK3BaupQ==", - "type": "package", - "path": "runtime.native.system.data.sqlclient.sni/4.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512", - "runtime.native.system.data.sqlclient.sni.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "runtime.native.System.IO.Compression/4.3.0": { - "sha512": "v/HwyslDJwFLsHwevuBsIW5uSVGx3aoMinU6SgM4vmIf0V7GIVA0kNvKVKdYCavE9CBmmzMFKyjSTXJqx5yYkQ==", - "type": "package", - "path": "runtime.native.system.io.compression/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.io.compression.4.3.0.nupkg.sha512", - "runtime.native.system.io.compression.nuspec" - ] - }, - "runtime.native.System.Net.Http/4.3.0": { - "sha512": "3dHltnVFR398crWINmbeQOie+wg22R56NJ4vPUrAXOESXmrdPLCcOcvf56t8Xcj9rq9qwlrNkvbePYzi1tt5GA==", - "type": "package", - "path": "runtime.native.system.net.http/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.net.http.4.3.0.nupkg.sha512", - "runtime.native.system.net.http.nuspec" - ] - }, - "runtime.native.System.Net.Security/4.3.0": { - "sha512": "xdsIagJutS/0Mt/OHtKVvDkOBwH9YeeOxEMBTSxunHwtUoI294ONmqVak623IDZU2vv0uM9aJVeDR6cn0LEmLQ==", - "type": "package", - "path": "runtime.native.system.net.security/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.net.security.4.3.0.nupkg.sha512", - "runtime.native.system.net.security.nuspec" - ] - }, - "runtime.native.System.Security.Cryptography.Apple/4.3.1": { - "sha512": "UPrVPlqPRSVZaB4ADmbsQ77KXn9ORiWXyA1RP2W2+byCh3bhgT1bQz0jbeOoog9/2oTQ5wWZSDSMeb74MjezcA==", - "type": "package", - "path": "runtime.native.system.security.cryptography.apple/4.3.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.security.cryptography.apple.4.3.1.nupkg.sha512", - "runtime.native.system.security.cryptography.apple.nuspec" - ] - }, - "runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "QR1OwtwehHxSeQvZKXe+iSd+d3XZNkEcuWMFYa2i0aG1l+lR739HPicKMlTbJst3spmeekDVBUS7SeS26s4U/g==", - "type": "package", - "path": "runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/_._", - "runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.native.system.security.cryptography.openssl.nuspec" - ] - }, - "runtime.opensuse.13.2-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "I+GNKGg2xCHueRd1m9PzeEW7WLbNNLznmTuEi8/vZX71HudUbx1UTwlGkiwMri7JLl8hGaIAWnA/GONhu+LOyQ==", - "type": "package", - "path": "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.opensuse.13.2-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/opensuse.13.2-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.opensuse.42.1-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "1Z3TAq1ytS1IBRtPXJvEUZdVsfWfeNEhBkbiOCGEl9wwAfsjP2lz3ZFDx5tq8p60/EqbS0HItG5piHuB71RjoA==", - "type": "package", - "path": "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.opensuse.42.1-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/opensuse.42.1-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.Apple/4.3.1": { - "sha512": "t15yGf5r6vMV1rB5O6TgfXKChtCaN3niwFw44M2ImX3eZ8yzueplqMqXPCbWzoBDHJVz9fE+9LFUGCsUmS2Jgg==", - "type": "package", - "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple/4.3.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.4.3.1.nupkg.sha512", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.apple.nuspec", - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.Apple.dylib" - ] - }, - "runtime.osx.10.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "6mU/cVmmHtQiDXhnzUImxIcDL48GbTk+TsptXyJA+MIOG9LRjPoAQC/qBFB7X+UNyK86bmvGwC8t+M66wsYC8w==", - "type": "package", - "path": "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.osx.10.10-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/osx.10.10-x64/native/System.Security.Cryptography.Native.OpenSsl.dylib" - ] - }, - "runtime.rhel.7-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "vjwG0GGcTW/PPg6KVud8F9GLWYuAV1rrw1BKAqY0oh4jcUqg15oYF1+qkGR2x2ZHM4DQnWKQ7cJgYbfncz/lYg==", - "type": "package", - "path": "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.rhel.7-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/rhel.7-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.ubuntu.14.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "7KMFpTkHC/zoExs+PwP8jDCWcrK9H6L7soowT80CUx3e+nxP/AFnq0AQAW5W76z2WYbLAYCRyPfwYFG6zkvQRw==", - "type": "package", - "path": "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.ubuntu.14.04-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/ubuntu.14.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.ubuntu.16.04-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "xrlmRCnKZJLHxyyLIqkZjNXqgxnKdZxfItrPkjI+6pkRo5lHX8YvSZlWrSI5AVwLMi4HbNWP7064hcAWeZKp5w==", - "type": "package", - "path": "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.ubuntu.16.04-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/ubuntu.16.04-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.ubuntu.16.10-x64.runtime.native.System.Security.Cryptography.OpenSsl/4.3.2": { - "sha512": "leXiwfiIkW7Gmn7cgnNcdtNAU70SjmKW3jxGj1iKHOvdn0zRWsgv/l2OJUO5zdGdiv2VRFnAsxxhDgMzofPdWg==", - "type": "package", - "path": "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.4.3.2.nupkg.sha512", - "runtime.ubuntu.16.10-x64.runtime.native.system.security.cryptography.openssl.nuspec", - "runtimes/ubuntu.16.10-x64/native/System.Security.Cryptography.Native.OpenSsl.so" - ] - }, - "runtime.win-arm64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "sha512": "i3DS3k1on6Qre8R0atBKu1U8aGk7wLCZwt8p0iLo1FwilMSlLhsC//1kpqfyb9AJuz0vIIHo1/KgCR1Q7+UTBQ==", - "type": "package", - "path": "runtime.win-arm64.runtime.native.system.data.sqlclient.sni/4.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.win-arm64.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512", - "runtime.win-arm64.runtime.native.system.data.sqlclient.sni.nuspec", - "runtimes/win-arm64/native/sni.dll", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "runtime.win-x64.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "sha512": "nU4qKCZlA/++QR5ssYki67krFmfU5Agfq6RsFuI9T+kNrwENVlCuzvrW9Dx6uTiLDKZE9NCiqk6dj54HIINF/Q==", - "type": "package", - "path": "runtime.win-x64.runtime.native.system.data.sqlclient.sni/4.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.win-x64.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512", - "runtime.win-x64.runtime.native.system.data.sqlclient.sni.nuspec", - "runtimes/win-x64/native/sni.dll", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "runtime.win-x86.runtime.native.System.Data.SqlClient.sni/4.4.0": { - "sha512": "QtS6OUI6E/UbSP8/BLtrx6zgtGeMeDikM6FMPL/PKCZwid+FIFZQOyKpVMocG1PRKCxKCQ0Sbsb0t9BiXX6RuQ==", - "type": "package", - "path": "runtime.win-x86.runtime.native.system.data.sqlclient.sni/4.4.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "runtime.win-x86.runtime.native.system.data.sqlclient.sni.4.4.0.nupkg.sha512", - "runtime.win-x86.runtime.native.system.data.sqlclient.sni.nuspec", - "runtimes/win-x86/native/sni.dll", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "Semver/2.0.4": { - "sha512": "H52a9cgrh4hkVxv5y99L9vk2NBtJB3ids/yavTHUvC0pUl3EsWEZAJmjBPFSt7jDtn3Q04+Nhtfo0J2xvsnhZQ==", - "type": "package", - "path": "semver/2.0.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net452/Semver.dll", - "lib/net452/Semver.xml", - "lib/netstandard1.1/Semver.dll", - "lib/netstandard1.1/Semver.xml", - "semver.2.0.4.nupkg.sha512", - "semver.nuspec" - ] - }, - "Serilog/2.6.0": { - "sha512": "hxHgHkza8ZowrvQ+jTEF2ypDWGP8NbNtxPQIx7ogTAM9fisDFo2+6kOaxCkleZLO8hiyq0kW5tDLkgm/dbj9uA==", - "type": "package", - "path": "serilog/2.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.dll", - "lib/net45/Serilog.xml", - "lib/net46/Serilog.dll", - "lib/net46/Serilog.xml", - "lib/netstandard1.0/Serilog.dll", - "lib/netstandard1.0/Serilog.xml", - "lib/netstandard1.3/Serilog.dll", - "lib/netstandard1.3/Serilog.xml", - "serilog.2.6.0.nupkg.sha512", - "serilog.nuspec" - ] - }, - "Serilog.AspNetCore/2.1.1": { - "sha512": "avgte2cI/pkGgUX3rX7PAvenMK2XkEgzovu6H9d6zT6NxPruoemt1Bx71Xng2n3nAYVttjSyNmQxKmy3LCGk2Q==", - "type": "package", - "path": "serilog.aspnetcore/2.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Serilog.AspNetCore.dll", - "lib/netstandard2.0/Serilog.AspNetCore.xml", - "serilog.aspnetcore.2.1.1.nupkg.sha512", - "serilog.aspnetcore.nuspec" - ] - }, - "Serilog.Enrichers.Environment/2.1.3": { - "sha512": "LclB09MAKLU14PXxlPiiejZxer1JhtbaVd1Zlb4EgllPYhzePYljx/jqW5uIXBe89hTtGlsCxjHLg9H6s1U24Q==", - "type": "package", - "path": "serilog.enrichers.environment/2.1.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.Enrichers.Environment.dll", - "lib/netstandard1.3/Serilog.Enrichers.Environment.dll", - "lib/netstandard1.5/Serilog.Enrichers.Environment.dll", - "serilog.enrichers.environment.2.1.3.nupkg.sha512", - "serilog.enrichers.environment.nuspec" - ] - }, - "Serilog.Enrichers.Thread/3.1.0": { - "sha512": "85lWsGRJpRxvKT6j/H67no55SUBsBIvp556TKuBTGhjtoPeq+L7j/sDWbgAtvT0p7u7/phJyX6j35PQ4Vtqw0g==", - "type": "package", - "path": "serilog.enrichers.thread/3.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.Enrichers.Thread.dll", - "lib/net45/Serilog.Enrichers.Thread.xml", - "lib/netstandard1.0/Serilog.Enrichers.Thread.dll", - "lib/netstandard1.0/Serilog.Enrichers.Thread.xml", - "lib/netstandard2.0/Serilog.Enrichers.Thread.dll", - "lib/netstandard2.0/Serilog.Enrichers.Thread.xml", - "serilog.enrichers.thread.3.1.0.nupkg.sha512", - "serilog.enrichers.thread.nuspec" - ] - }, - "Serilog.Extensions.Logging/2.0.4": { - "sha512": "C8Vf9Wj1M+wGilChTV+OhE4v5ZCDzQfHjLKj2yNDMkXf/zgUKeAUZfbrVrt/c+flXP8M7/SHWBOXTkuPgubFsA==", - "type": "package", - "path": "serilog.extensions.logging/2.0.4", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.Extensions.Logging.dll", - "lib/net45/Serilog.Extensions.Logging.xml", - "lib/net46/Serilog.Extensions.Logging.dll", - "lib/net46/Serilog.Extensions.Logging.xml", - "lib/net461/Serilog.Extensions.Logging.dll", - "lib/net461/Serilog.Extensions.Logging.xml", - "lib/netstandard1.3/Serilog.Extensions.Logging.dll", - "lib/netstandard1.3/Serilog.Extensions.Logging.xml", - "lib/netstandard2.0/Serilog.Extensions.Logging.dll", - "lib/netstandard2.0/Serilog.Extensions.Logging.xml", - "serilog.extensions.logging.2.0.4.nupkg.sha512", - "serilog.extensions.logging.nuspec" - ] - }, - "Serilog.Settings.Configuration/3.1.0": { - "sha512": "BS+G1dhThTHBOYm8R21JNlR+Nh7ETAOlJuL1P6te1rOG98eV1vos5EyWRTGr0AbHgySxsGu1Q/evfFxS9+Gk1Q==", - "type": "package", - "path": "serilog.settings.configuration/3.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net451/Serilog.Settings.Configuration.dll", - "lib/net451/Serilog.Settings.Configuration.xml", - "lib/net461/Serilog.Settings.Configuration.dll", - "lib/net461/Serilog.Settings.Configuration.xml", - "lib/netstandard2.0/Serilog.Settings.Configuration.dll", - "lib/netstandard2.0/Serilog.Settings.Configuration.xml", - "serilog.settings.configuration.3.1.0.nupkg.sha512", - "serilog.settings.configuration.nuspec" - ] - }, - "Serilog.Sinks.Console/3.1.1": { - "sha512": "56mI5AqvyF/i/c2451nvV71kq370XOCE4Uu5qiaJ295sOhMb9q3BWwG7mWLOVSnmpWiq0SBT3SXfgRXGNP6vzA==", - "type": "package", - "path": "serilog.sinks.console/3.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.Sinks.Console.dll", - "lib/net45/Serilog.Sinks.Console.xml", - "lib/netcoreapp1.1/Serilog.Sinks.Console.dll", - "lib/netcoreapp1.1/Serilog.Sinks.Console.xml", - "lib/netstandard1.3/Serilog.Sinks.Console.dll", - "lib/netstandard1.3/Serilog.Sinks.Console.xml", - "serilog.sinks.console.3.1.1.nupkg.sha512", - "serilog.sinks.console.nuspec" - ] - }, - "Serilog.Sinks.File/4.0.0": { - "sha512": "vBj43RkAbeP1dzoPFR2+LfV5GevDRPDq6265JJBv223lMvT9rfdwe/S/I9ow7aZSLYKfw4qPDw6NW8YwjbDbvg==", - "type": "package", - "path": "serilog.sinks.file/4.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/Serilog.Sinks.File.dll", - "lib/net45/Serilog.Sinks.File.xml", - "lib/netstandard1.3/Serilog.Sinks.File.dll", - "lib/netstandard1.3/Serilog.Sinks.File.xml", - "serilog.sinks.file.4.0.0.nupkg.sha512", - "serilog.sinks.file.nuspec" - ] - }, - "SharpRepository.Ioc.Autofac/2.0.4.2": { - "sha512": "hYQyngQI3vEvDge/v0F88ql2NR18+r1YhV68yAifBMR/LXp3J9GLVlFjNUS8Q/Bsopyfq/U9Nd4ji54E7ssq0g==", - "type": "package", - "path": "sharprepository.ioc.autofac/2.0.4.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net451/SharpRepository.Ioc.Autofac.dll", - "lib/netstandard1.3/SharpRepository.Ioc.Autofac.dll", - "sharprepository.ioc.autofac.2.0.4.2.nupkg.sha512", - "sharprepository.ioc.autofac.nuspec" - ] - }, - "SharpRepository.Repository/2.0.4.6": { - "sha512": "vEu3Rp1XAqv7NorkigOhwfkFEi1Dhf+R5xUPVNv4C7/drgO2QCD4mf4sg66D/nkY1KJAP9VfsjEwPQOjycMMJg==", - "type": "package", - "path": "sharprepository.repository/2.0.4.6", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "content/repository.default.json", - "contentFiles/any/net451/repository.default.json", - "contentFiles/any/netstandard1.3/repository.default.json", - "contentFiles/any/netstandard2.0/repository.default.json", - "lib/net451/SharpRepository.Repository.dll", - "lib/netstandard1.3/SharpRepository.Repository.dll", - "lib/netstandard2.0/SharpRepository.Repository.dll", - "sharprepository.repository.2.0.4.6.nupkg.sha512", - "sharprepository.repository.nuspec" - ] - }, - "SharpRepository.XmlRepository/2.0.1-alpha3": { - "sha512": "k0Lvd0EJgvcf4ohY3+NKWJ7td/n+qDRZZD2S3vwE3KgaXUwRzA+kTD9kVrXa9xogoQeBB69AuxpD5cKWEDj66Q==", - "type": "package", - "path": "sharprepository.xmlrepository/2.0.1-alpha3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "content/repository.xml.json", - "contentFiles/any/net451/repository.xml.json", - "contentFiles/any/netstandard2.0/repository.xml.json", - "lib/net451/SharpRepository.XmlRepository.dll", - "lib/netstandard2.0/SharpRepository.XmlRepository.dll", - "sharprepository.xmlrepository.2.0.1-alpha3.nupkg.sha512", - "sharprepository.xmlrepository.nuspec" - ] - }, - "SharpZipLib/1.0.0": { - "sha512": "YuYztmY3jEb21F6e5LIPHJjApdtzdCPQ284UzsCKNfkgW71bukFHJES6RbKi+wm053XzFg0LX5/2vj/9gl8F/g==", - "type": "package", - "path": "sharpziplib/1.0.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/ICSharpCode.SharpZipLib.dll", - "lib/net45/ICSharpCode.SharpZipLib.xml", - "lib/netstandard2.0/ICSharpCode.SharpZipLib.dll", - "lib/netstandard2.0/ICSharpCode.SharpZipLib.xml", - "sharpziplib.1.0.0.nupkg.sha512", - "sharpziplib.nuspec" - ] - }, - "SimpleBase/1.3.1": { - "sha512": "TqqQc+WFg3s10/PdnI2Dio0U7xEmVXhkf+ey4tOGZzjyd054rnNOLNVahJhMYmfBQdS7/jkVqC8xzLm/bkR3EA==", - "type": "package", - "path": "simplebase/1.3.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/SimpleBase.dll", - "lib/net45/SimpleBase.pdb", - "lib/netstandard1.3/SimpleBase.dll", - "lib/netstandard1.3/SimpleBase.pdb", - "simplebase.1.3.1.nupkg.sha512", - "simplebase.nuspec" - ] - }, - "Swashbuckle.AspNetCore.Swagger/4.0.1": { - "sha512": "2+dXBiOvwjNmkMkBOqGU9ShBJXQp6+UN/Kxfk3HLxwsof9zzgRZ+3rOdjHQuYiY7t1Nl7wlCgn9HbmJyZGhdaQ==", - "type": "package", - "path": "swashbuckle.aspnetcore.swagger/4.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Swashbuckle.AspNetCore.Swagger.dll", - "lib/netstandard2.0/Swashbuckle.AspNetCore.Swagger.xml", - "swashbuckle.aspnetcore.swagger.4.0.1.nupkg.sha512", - "swashbuckle.aspnetcore.swagger.nuspec" - ] - }, - "Swashbuckle.AspNetCore.SwaggerGen/4.0.1": { - "sha512": "opm/wgG8u987ZuAUtL1E8XrJig+UbGYbFmz8VnA8Vn3AqZyQZy4k243X9egz1iWl/B6sNcgMlFyv3wkdmjJX6g==", - "type": "package", - "path": "swashbuckle.aspnetcore.swaggergen/4.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerGen.dll", - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerGen.xml", - "swashbuckle.aspnetcore.swaggergen.4.0.1.nupkg.sha512", - "swashbuckle.aspnetcore.swaggergen.nuspec" - ] - }, - "Swashbuckle.AspNetCore.SwaggerUI/4.0.1": { - "sha512": "EjPeIYSMLr5FrY4sVJiWrzIQe07Hriv8tOztJa7US88im/tO+tX70y7OJ2Cr8QYojc7IotjZSH1lD8j44DLnrQ==", - "type": "package", - "path": "swashbuckle.aspnetcore.swaggerui/4.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerUI.dll", - "lib/netstandard2.0/Swashbuckle.AspNetCore.SwaggerUI.xml", - "swashbuckle.aspnetcore.swaggerui.4.0.1.nupkg.sha512", - "swashbuckle.aspnetcore.swaggerui.nuspec" - ] - }, - "System.AppContext/4.3.0": { - "sha512": "DW6mMAYwRwj+rizAWQ0s3Zkye2giEIIrsoA6yEL99NjVcXDXlHwAbuxLVofJQnaEeKfsEJdFRy85RtIwcySD6A==", - "type": "package", - "path": "system.appcontext/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.AppContext.dll", - "lib/net463/System.AppContext.dll", - "lib/netcore50/System.AppContext.dll", - "lib/netstandard1.6/System.AppContext.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.AppContext.dll", - "ref/net463/System.AppContext.dll", - "ref/netstandard/_._", - "ref/netstandard1.3/System.AppContext.dll", - "ref/netstandard1.3/System.AppContext.xml", - "ref/netstandard1.3/de/System.AppContext.xml", - "ref/netstandard1.3/es/System.AppContext.xml", - "ref/netstandard1.3/fr/System.AppContext.xml", - "ref/netstandard1.3/it/System.AppContext.xml", - "ref/netstandard1.3/ja/System.AppContext.xml", - "ref/netstandard1.3/ko/System.AppContext.xml", - "ref/netstandard1.3/ru/System.AppContext.xml", - "ref/netstandard1.3/zh-hans/System.AppContext.xml", - "ref/netstandard1.3/zh-hant/System.AppContext.xml", - "ref/netstandard1.6/System.AppContext.dll", - "ref/netstandard1.6/System.AppContext.xml", - "ref/netstandard1.6/de/System.AppContext.xml", - "ref/netstandard1.6/es/System.AppContext.xml", - "ref/netstandard1.6/fr/System.AppContext.xml", - "ref/netstandard1.6/it/System.AppContext.xml", - "ref/netstandard1.6/ja/System.AppContext.xml", - "ref/netstandard1.6/ko/System.AppContext.xml", - "ref/netstandard1.6/ru/System.AppContext.xml", - "ref/netstandard1.6/zh-hans/System.AppContext.xml", - "ref/netstandard1.6/zh-hant/System.AppContext.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.AppContext.dll", - "system.appcontext.4.3.0.nupkg.sha512", - "system.appcontext.nuspec" - ] - }, - "System.Buffers/4.5.0": { - "sha512": "09yL/QiPEDZI9JLg0R0OcGe/0ycFm69QN16DCvXkqkIorCC1Y2EKwk5KvSlfMmGse+LcKkD3H+Cra7fFkxHXEg==", - "type": "package", - "path": "system.buffers/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netcoreapp2.0/_._", - "lib/netstandard1.1/System.Buffers.dll", - "lib/netstandard1.1/System.Buffers.xml", - "lib/netstandard2.0/System.Buffers.dll", - "lib/netstandard2.0/System.Buffers.xml", - "lib/uap10.0.16299/_._", - "ref/net45/System.Buffers.dll", - "ref/net45/System.Buffers.xml", - "ref/netcoreapp2.0/_._", - "ref/netstandard1.1/System.Buffers.dll", - "ref/netstandard1.1/System.Buffers.xml", - "ref/netstandard2.0/System.Buffers.dll", - "ref/netstandard2.0/System.Buffers.xml", - "ref/uap10.0.16299/_._", - "system.buffers.4.5.0.nupkg.sha512", - "system.buffers.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Collections/4.3.0": { - "sha512": "ynuVLTDaFIfKTkOqUigXte4m5+EFNwYoEBEvxnp1EnZsOdQC85S7BCbREIu8+bu2Tpzh9a9zbvIVpRo15V8FGw==", - "type": "package", - "path": "system.collections/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Collections.dll", - "ref/netcore50/System.Collections.xml", - "ref/netcore50/de/System.Collections.xml", - "ref/netcore50/es/System.Collections.xml", - "ref/netcore50/fr/System.Collections.xml", - "ref/netcore50/it/System.Collections.xml", - "ref/netcore50/ja/System.Collections.xml", - "ref/netcore50/ko/System.Collections.xml", - "ref/netcore50/ru/System.Collections.xml", - "ref/netcore50/zh-hans/System.Collections.xml", - "ref/netcore50/zh-hant/System.Collections.xml", - "ref/netstandard1.0/System.Collections.dll", - "ref/netstandard1.0/System.Collections.xml", - "ref/netstandard1.0/de/System.Collections.xml", - "ref/netstandard1.0/es/System.Collections.xml", - "ref/netstandard1.0/fr/System.Collections.xml", - "ref/netstandard1.0/it/System.Collections.xml", - "ref/netstandard1.0/ja/System.Collections.xml", - "ref/netstandard1.0/ko/System.Collections.xml", - "ref/netstandard1.0/ru/System.Collections.xml", - "ref/netstandard1.0/zh-hans/System.Collections.xml", - "ref/netstandard1.0/zh-hant/System.Collections.xml", - "ref/netstandard1.3/System.Collections.dll", - "ref/netstandard1.3/System.Collections.xml", - "ref/netstandard1.3/de/System.Collections.xml", - "ref/netstandard1.3/es/System.Collections.xml", - "ref/netstandard1.3/fr/System.Collections.xml", - "ref/netstandard1.3/it/System.Collections.xml", - "ref/netstandard1.3/ja/System.Collections.xml", - "ref/netstandard1.3/ko/System.Collections.xml", - "ref/netstandard1.3/ru/System.Collections.xml", - "ref/netstandard1.3/zh-hans/System.Collections.xml", - "ref/netstandard1.3/zh-hant/System.Collections.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.collections.4.3.0.nupkg.sha512", - "system.collections.nuspec" - ] - }, - "System.Collections.Concurrent/4.3.0": { - "sha512": "NcGqPmNiFv5dwuvrUEKT5prWNV0m4iRTrwYK+U2CefqpO9z+EnrssLMWx+fZGFvKxy6ZSYTv238thRXx9Vz2gg==", - "type": "package", - "path": "system.collections.concurrent/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Collections.Concurrent.dll", - "lib/netstandard1.3/System.Collections.Concurrent.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Collections.Concurrent.dll", - "ref/netcore50/System.Collections.Concurrent.xml", - "ref/netcore50/de/System.Collections.Concurrent.xml", - "ref/netcore50/es/System.Collections.Concurrent.xml", - "ref/netcore50/fr/System.Collections.Concurrent.xml", - "ref/netcore50/it/System.Collections.Concurrent.xml", - "ref/netcore50/ja/System.Collections.Concurrent.xml", - "ref/netcore50/ko/System.Collections.Concurrent.xml", - "ref/netcore50/ru/System.Collections.Concurrent.xml", - "ref/netcore50/zh-hans/System.Collections.Concurrent.xml", - "ref/netcore50/zh-hant/System.Collections.Concurrent.xml", - "ref/netstandard1.1/System.Collections.Concurrent.dll", - "ref/netstandard1.1/System.Collections.Concurrent.xml", - "ref/netstandard1.1/de/System.Collections.Concurrent.xml", - "ref/netstandard1.1/es/System.Collections.Concurrent.xml", - "ref/netstandard1.1/fr/System.Collections.Concurrent.xml", - "ref/netstandard1.1/it/System.Collections.Concurrent.xml", - "ref/netstandard1.1/ja/System.Collections.Concurrent.xml", - "ref/netstandard1.1/ko/System.Collections.Concurrent.xml", - "ref/netstandard1.1/ru/System.Collections.Concurrent.xml", - "ref/netstandard1.1/zh-hans/System.Collections.Concurrent.xml", - "ref/netstandard1.1/zh-hant/System.Collections.Concurrent.xml", - "ref/netstandard1.3/System.Collections.Concurrent.dll", - "ref/netstandard1.3/System.Collections.Concurrent.xml", - "ref/netstandard1.3/de/System.Collections.Concurrent.xml", - "ref/netstandard1.3/es/System.Collections.Concurrent.xml", - "ref/netstandard1.3/fr/System.Collections.Concurrent.xml", - "ref/netstandard1.3/it/System.Collections.Concurrent.xml", - "ref/netstandard1.3/ja/System.Collections.Concurrent.xml", - "ref/netstandard1.3/ko/System.Collections.Concurrent.xml", - "ref/netstandard1.3/ru/System.Collections.Concurrent.xml", - "ref/netstandard1.3/zh-hans/System.Collections.Concurrent.xml", - "ref/netstandard1.3/zh-hant/System.Collections.Concurrent.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.collections.concurrent.4.3.0.nupkg.sha512", - "system.collections.concurrent.nuspec" - ] - }, - "System.Collections.Immutable/1.5.0": { - "sha512": "T5XGQlcHhEO75Qx38GGCUDPdk4n/7yrRmTgy4yczzJV8alPHaxPU55TBC2UFrkQ42bu34sZPfK0dU+nWZUOEJA==", - "type": "package", - "path": "system.collections.immutable/1.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Collections.Immutable.dll", - "lib/netstandard1.0/System.Collections.Immutable.xml", - "lib/netstandard1.3/System.Collections.Immutable.dll", - "lib/netstandard1.3/System.Collections.Immutable.xml", - "lib/netstandard2.0/System.Collections.Immutable.dll", - "lib/netstandard2.0/System.Collections.Immutable.xml", - "lib/portable-net45+win8+wp8+wpa81/System.Collections.Immutable.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Collections.Immutable.xml", - "system.collections.immutable.1.5.0.nupkg.sha512", - "system.collections.immutable.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Collections.NonGeneric/4.3.0": { - "sha512": "3Gq/53iz6gjpn1C3kRKlFyjmifNTsIIjQ1G59bG+S2AaC204oEwhONBbW92D1vPZG1Puhu0RkiXBXkaDw4v5jA==", - "type": "package", - "path": "system.collections.nongeneric/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Collections.NonGeneric.dll", - "lib/netstandard1.3/System.Collections.NonGeneric.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Collections.NonGeneric.dll", - "ref/netstandard1.3/System.Collections.NonGeneric.dll", - "ref/netstandard1.3/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/de/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/es/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/fr/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/it/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/ja/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/ko/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/ru/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/zh-hans/System.Collections.NonGeneric.xml", - "ref/netstandard1.3/zh-hant/System.Collections.NonGeneric.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.collections.nongeneric.4.3.0.nupkg.sha512", - "system.collections.nongeneric.nuspec" - ] - }, - "System.Collections.Specialized/4.3.0": { - "sha512": "x6JXy9qBLWwMRLKdi7XMT1zy08uBV06x3JSn796YCsJsX/rbfMNSH+exlDd2agRsg8vy5+pZo2Q1woit2BQrVw==", - "type": "package", - "path": "system.collections.specialized/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Collections.Specialized.dll", - "lib/netstandard1.3/System.Collections.Specialized.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Collections.Specialized.dll", - "ref/netstandard1.3/System.Collections.Specialized.dll", - "ref/netstandard1.3/System.Collections.Specialized.xml", - "ref/netstandard1.3/de/System.Collections.Specialized.xml", - "ref/netstandard1.3/es/System.Collections.Specialized.xml", - "ref/netstandard1.3/fr/System.Collections.Specialized.xml", - "ref/netstandard1.3/it/System.Collections.Specialized.xml", - "ref/netstandard1.3/ja/System.Collections.Specialized.xml", - "ref/netstandard1.3/ko/System.Collections.Specialized.xml", - "ref/netstandard1.3/ru/System.Collections.Specialized.xml", - "ref/netstandard1.3/zh-hans/System.Collections.Specialized.xml", - "ref/netstandard1.3/zh-hant/System.Collections.Specialized.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.collections.specialized.4.3.0.nupkg.sha512", - "system.collections.specialized.nuspec" - ] - }, - "System.ComponentModel/4.0.1": { - "sha512": "oBZFnm7seFiVfugsIyOvQCWobNZs7FzqDV/B7tx20Ep/l3UUFCPDkdTnCNaJZTU27zjeODmy2C/cP60u3D4c9w==", - "type": "package", - "path": "system.componentmodel/4.0.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.ComponentModel.dll", - "lib/netstandard1.3/System.ComponentModel.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.ComponentModel.dll", - "ref/netcore50/System.ComponentModel.xml", - "ref/netcore50/de/System.ComponentModel.xml", - "ref/netcore50/es/System.ComponentModel.xml", - "ref/netcore50/fr/System.ComponentModel.xml", - "ref/netcore50/it/System.ComponentModel.xml", - "ref/netcore50/ja/System.ComponentModel.xml", - "ref/netcore50/ko/System.ComponentModel.xml", - "ref/netcore50/ru/System.ComponentModel.xml", - "ref/netcore50/zh-hans/System.ComponentModel.xml", - "ref/netcore50/zh-hant/System.ComponentModel.xml", - "ref/netstandard1.0/System.ComponentModel.dll", - "ref/netstandard1.0/System.ComponentModel.xml", - "ref/netstandard1.0/de/System.ComponentModel.xml", - "ref/netstandard1.0/es/System.ComponentModel.xml", - "ref/netstandard1.0/fr/System.ComponentModel.xml", - "ref/netstandard1.0/it/System.ComponentModel.xml", - "ref/netstandard1.0/ja/System.ComponentModel.xml", - "ref/netstandard1.0/ko/System.ComponentModel.xml", - "ref/netstandard1.0/ru/System.ComponentModel.xml", - "ref/netstandard1.0/zh-hans/System.ComponentModel.xml", - "ref/netstandard1.0/zh-hant/System.ComponentModel.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.componentmodel.4.0.1.nupkg.sha512", - "system.componentmodel.nuspec" - ] - }, - "System.ComponentModel.Annotations/4.5.0": { - "sha512": "f1ApUHGWq/lJC8PZE7JqbA3tiY7ZngZQO2mbYfCG0JlQVVUqqmVMAy0fMvAwEuG639M47ELdP6PQxc5OIo6i6A==", - "type": "package", - "path": "system.componentmodel.annotations/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net461/System.ComponentModel.Annotations.dll", - "lib/netcore50/System.ComponentModel.Annotations.dll", - "lib/netcoreapp2.0/_._", - "lib/netstandard1.4/System.ComponentModel.Annotations.dll", - "lib/netstandard2.0/System.ComponentModel.Annotations.dll", - "lib/portable-net45+win8/_._", - "lib/uap10.0.16299/_._", - "lib/win8/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net461/System.ComponentModel.Annotations.dll", - "ref/net461/System.ComponentModel.Annotations.xml", - "ref/netcore50/System.ComponentModel.Annotations.dll", - "ref/netcore50/System.ComponentModel.Annotations.xml", - "ref/netcore50/de/System.ComponentModel.Annotations.xml", - "ref/netcore50/es/System.ComponentModel.Annotations.xml", - "ref/netcore50/fr/System.ComponentModel.Annotations.xml", - "ref/netcore50/it/System.ComponentModel.Annotations.xml", - "ref/netcore50/ja/System.ComponentModel.Annotations.xml", - "ref/netcore50/ko/System.ComponentModel.Annotations.xml", - "ref/netcore50/ru/System.ComponentModel.Annotations.xml", - "ref/netcore50/zh-hans/System.ComponentModel.Annotations.xml", - "ref/netcore50/zh-hant/System.ComponentModel.Annotations.xml", - "ref/netcoreapp2.0/_._", - "ref/netstandard1.1/System.ComponentModel.Annotations.dll", - "ref/netstandard1.1/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/de/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/es/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/fr/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/it/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/ja/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/ko/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/ru/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/zh-hans/System.ComponentModel.Annotations.xml", - "ref/netstandard1.1/zh-hant/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/System.ComponentModel.Annotations.dll", - "ref/netstandard1.3/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/de/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/es/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/fr/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/it/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/ja/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/ko/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/ru/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/zh-hans/System.ComponentModel.Annotations.xml", - "ref/netstandard1.3/zh-hant/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/System.ComponentModel.Annotations.dll", - "ref/netstandard1.4/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/de/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/es/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/fr/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/it/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/ja/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/ko/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/ru/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/zh-hans/System.ComponentModel.Annotations.xml", - "ref/netstandard1.4/zh-hant/System.ComponentModel.Annotations.xml", - "ref/netstandard2.0/System.ComponentModel.Annotations.dll", - "ref/netstandard2.0/System.ComponentModel.Annotations.xml", - "ref/portable-net45+win8/_._", - "ref/uap10.0.16299/_._", - "ref/win8/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.componentmodel.annotations.4.5.0.nupkg.sha512", - "system.componentmodel.annotations.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Composition/1.2.0": { - "sha512": "nTgIj77StlLM7CW3uFM3B/0Yen5udzaeSQcdSCVV3wIlRGYsXYLjZWTYa9m8IBjQiyZKsukKYaogqhOa6QUlDA==", - "type": "package", - "path": "system.composition/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "system.composition.1.2.0.nupkg.sha512", - "system.composition.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Composition.AttributedModel/1.2.0": { - "sha512": "IQ2bn1BR/Q7gapjnXR/HGh0BMtjYVU0t0uPZ3LXE4yfwjM7x/HcImJxwwhUtnL+YWU5/pTOhzZnqsjwKJpWaug==", - "type": "package", - "path": "system.composition.attributedmodel/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Composition.AttributedModel.dll", - "lib/netstandard2.0/System.Composition.AttributedModel.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Composition.AttributedModel.dll", - "system.composition.attributedmodel.1.2.0.nupkg.sha512", - "system.composition.attributedmodel.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Composition.Convention/1.2.0": { - "sha512": "g9PSAdL/0dT3GZbdwt5r238RLHfnn+ujRVhoOGvVNjbbhlgZeKcDA+zsje4Y81csMywAPsDXkeXrBigtjINurg==", - "type": "package", - "path": "system.composition.convention/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Composition.Convention.dll", - "lib/netstandard2.0/System.Composition.Convention.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Composition.Convention.dll", - "system.composition.convention.1.2.0.nupkg.sha512", - "system.composition.convention.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Composition.Hosting/1.2.0": { - "sha512": "NQa4OanHFuWVpMuj3+0RnoAq2v+5KQNA3+EYuhmuDbOfR06o7rYjzs9FHP0XWJWN85vqnM76dgAgj46OYsDV8A==", - "type": "package", - "path": "system.composition.hosting/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Composition.Hosting.dll", - "lib/netstandard2.0/System.Composition.Hosting.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Composition.Hosting.dll", - "system.composition.hosting.1.2.0.nupkg.sha512", - "system.composition.hosting.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Composition.Runtime/1.2.0": { - "sha512": "F8Ef3y9/JKbK4lEqJScFnfhT8/CwboGS890a/Js9E11wb1N6rl63pU8wxRPmy2MUUUHSafxrF3ooIh94pNEF0g==", - "type": "package", - "path": "system.composition.runtime/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Composition.Runtime.dll", - "lib/netstandard2.0/System.Composition.Runtime.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Composition.Runtime.dll", - "system.composition.runtime.1.2.0.nupkg.sha512", - "system.composition.runtime.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Composition.TypedParts/1.2.0": { - "sha512": "cLjoUGnaLRkJSwL6FLEx3aJanDgwEtyoEqf9cE6Z5ipbjNXAlk7W11uwNfHaECxdPa/QfGbvaRd4i24gxc5ygg==", - "type": "package", - "path": "system.composition.typedparts/1.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Composition.TypedParts.dll", - "lib/netstandard2.0/System.Composition.TypedParts.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Composition.TypedParts.dll", - "system.composition.typedparts.1.2.0.nupkg.sha512", - "system.composition.typedparts.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Console/4.3.0": { - "sha512": "oIpoSlg8mzJ4zjK+EAfa5JX52HJUZmOS95TvEgMHnzM819OIwolE/6NvtJ8Mi7IfQscPbh18HAOSDfbQ0RMMgg==", - "type": "package", - "path": "system.console/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Console.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Console.dll", - "ref/netstandard1.3/System.Console.dll", - "ref/netstandard1.3/System.Console.xml", - "ref/netstandard1.3/de/System.Console.xml", - "ref/netstandard1.3/es/System.Console.xml", - "ref/netstandard1.3/fr/System.Console.xml", - "ref/netstandard1.3/it/System.Console.xml", - "ref/netstandard1.3/ja/System.Console.xml", - "ref/netstandard1.3/ko/System.Console.xml", - "ref/netstandard1.3/ru/System.Console.xml", - "ref/netstandard1.3/zh-hans/System.Console.xml", - "ref/netstandard1.3/zh-hant/System.Console.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.console.4.3.0.nupkg.sha512", - "system.console.nuspec" - ] - }, - "System.Data.SqlClient/4.5.0": { - "sha512": "ap20AC4BNIEo9GGJAYjjZWCutPOP70bXtFpJ3Bl1LpFY+8bdFtFITads/5CYeTMyJAH/fCrQsIcKWnwotATSdg==", - "type": "package", - "path": "system.data.sqlclient/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net451/System.Data.SqlClient.dll", - "lib/net46/System.Data.SqlClient.dll", - "lib/net461/System.Data.SqlClient.dll", - "lib/netcoreapp2.1/System.Data.SqlClient.dll", - "lib/netstandard1.2/System.Data.SqlClient.dll", - "lib/netstandard1.3/System.Data.SqlClient.dll", - "lib/netstandard2.0/System.Data.SqlClient.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net451/System.Data.SqlClient.dll", - "ref/net46/System.Data.SqlClient.dll", - "ref/net461/System.Data.SqlClient.dll", - "ref/net461/System.Data.SqlClient.xml", - "ref/netcoreapp2.1/System.Data.SqlClient.dll", - "ref/netcoreapp2.1/System.Data.SqlClient.xml", - "ref/netstandard1.2/System.Data.SqlClient.dll", - "ref/netstandard1.2/System.Data.SqlClient.xml", - "ref/netstandard1.2/de/System.Data.SqlClient.xml", - "ref/netstandard1.2/es/System.Data.SqlClient.xml", - "ref/netstandard1.2/fr/System.Data.SqlClient.xml", - "ref/netstandard1.2/it/System.Data.SqlClient.xml", - "ref/netstandard1.2/ja/System.Data.SqlClient.xml", - "ref/netstandard1.2/ko/System.Data.SqlClient.xml", - "ref/netstandard1.2/ru/System.Data.SqlClient.xml", - "ref/netstandard1.2/zh-hans/System.Data.SqlClient.xml", - "ref/netstandard1.2/zh-hant/System.Data.SqlClient.xml", - "ref/netstandard1.3/System.Data.SqlClient.dll", - "ref/netstandard1.3/System.Data.SqlClient.xml", - "ref/netstandard1.3/de/System.Data.SqlClient.xml", - "ref/netstandard1.3/es/System.Data.SqlClient.xml", - "ref/netstandard1.3/fr/System.Data.SqlClient.xml", - "ref/netstandard1.3/it/System.Data.SqlClient.xml", - "ref/netstandard1.3/ja/System.Data.SqlClient.xml", - "ref/netstandard1.3/ko/System.Data.SqlClient.xml", - "ref/netstandard1.3/ru/System.Data.SqlClient.xml", - "ref/netstandard1.3/zh-hans/System.Data.SqlClient.xml", - "ref/netstandard1.3/zh-hant/System.Data.SqlClient.xml", - "ref/netstandard2.0/System.Data.SqlClient.dll", - "ref/netstandard2.0/System.Data.SqlClient.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netcoreapp2.1/System.Data.SqlClient.dll", - "runtimes/unix/lib/netstandard1.3/System.Data.SqlClient.dll", - "runtimes/unix/lib/netstandard2.0/System.Data.SqlClient.dll", - "runtimes/win/lib/net451/System.Data.SqlClient.dll", - "runtimes/win/lib/net46/System.Data.SqlClient.dll", - "runtimes/win/lib/net461/System.Data.SqlClient.dll", - "runtimes/win/lib/netcoreapp2.1/System.Data.SqlClient.dll", - "runtimes/win/lib/netstandard1.3/System.Data.SqlClient.dll", - "runtimes/win/lib/netstandard2.0/System.Data.SqlClient.dll", - "runtimes/win/lib/uap10.0.16299/System.Data.SqlClient.dll", - "runtimes/win/lib/uap10.0.16300/System.Data.SqlClient.dll", - "system.data.sqlclient.4.5.0.nupkg.sha512", - "system.data.sqlclient.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Diagnostics.Contracts/4.3.0": { - "sha512": "hQjC+dSe1rBacyz3Qh4hooO4bWhG3/pzIL4bsMHPsLCWAQ5xfNYIuB7sU7pVZSV2v/p7DcrX6U7qAjZKJSaigg==", - "type": "package", - "path": "system.diagnostics.contracts/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Diagnostics.Contracts.dll", - "lib/netstandard1.0/System.Diagnostics.Contracts.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Diagnostics.Contracts.dll", - "ref/netcore50/System.Diagnostics.Contracts.xml", - "ref/netcore50/de/System.Diagnostics.Contracts.xml", - "ref/netcore50/es/System.Diagnostics.Contracts.xml", - "ref/netcore50/fr/System.Diagnostics.Contracts.xml", - "ref/netcore50/it/System.Diagnostics.Contracts.xml", - "ref/netcore50/ja/System.Diagnostics.Contracts.xml", - "ref/netcore50/ko/System.Diagnostics.Contracts.xml", - "ref/netcore50/ru/System.Diagnostics.Contracts.xml", - "ref/netcore50/zh-hans/System.Diagnostics.Contracts.xml", - "ref/netcore50/zh-hant/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/System.Diagnostics.Contracts.dll", - "ref/netstandard1.0/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/de/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/es/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/fr/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/it/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/ja/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/ko/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/ru/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/zh-hans/System.Diagnostics.Contracts.xml", - "ref/netstandard1.0/zh-hant/System.Diagnostics.Contracts.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Diagnostics.Contracts.dll", - "system.diagnostics.contracts.4.3.0.nupkg.sha512", - "system.diagnostics.contracts.nuspec" - ] - }, - "System.Diagnostics.Debug/4.3.0": { - "sha512": "bFj+HjYY5/h2hMHOp+/H07Gb19+NJTX54ntixS9EHxG2eyEiXWvNYvQJ4CwqFiMcTbGb4zuPq1ubClyGYN2rJA==", - "type": "package", - "path": "system.diagnostics.debug/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Diagnostics.Debug.dll", - "ref/netcore50/System.Diagnostics.Debug.xml", - "ref/netcore50/de/System.Diagnostics.Debug.xml", - "ref/netcore50/es/System.Diagnostics.Debug.xml", - "ref/netcore50/fr/System.Diagnostics.Debug.xml", - "ref/netcore50/it/System.Diagnostics.Debug.xml", - "ref/netcore50/ja/System.Diagnostics.Debug.xml", - "ref/netcore50/ko/System.Diagnostics.Debug.xml", - "ref/netcore50/ru/System.Diagnostics.Debug.xml", - "ref/netcore50/zh-hans/System.Diagnostics.Debug.xml", - "ref/netcore50/zh-hant/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/System.Diagnostics.Debug.dll", - "ref/netstandard1.0/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/de/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/es/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/fr/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/it/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/ja/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/ko/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/ru/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/zh-hans/System.Diagnostics.Debug.xml", - "ref/netstandard1.0/zh-hant/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/System.Diagnostics.Debug.dll", - "ref/netstandard1.3/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/de/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/es/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/fr/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/it/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/ja/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/ko/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/ru/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/zh-hans/System.Diagnostics.Debug.xml", - "ref/netstandard1.3/zh-hant/System.Diagnostics.Debug.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.diagnostics.debug.4.3.0.nupkg.sha512", - "system.diagnostics.debug.nuspec" - ] - }, - "System.Diagnostics.DiagnosticSource/4.5.0": { - "sha512": "zgwboUmA+bhTe+Sk+cLaqqHHDvdM2v0Ra//St8pIzx8+RJeV/1F1SNGkLCf+mo+eZv++HJ/RG6HWKNgirJN2NA==", - "type": "package", - "path": "system.diagnostics.diagnosticsource/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net45/System.Diagnostics.DiagnosticSource.dll", - "lib/net45/System.Diagnostics.DiagnosticSource.xml", - "lib/net46/System.Diagnostics.DiagnosticSource.dll", - "lib/net46/System.Diagnostics.DiagnosticSource.xml", - "lib/netstandard1.1/System.Diagnostics.DiagnosticSource.dll", - "lib/netstandard1.1/System.Diagnostics.DiagnosticSource.xml", - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.dll", - "lib/netstandard1.3/System.Diagnostics.DiagnosticSource.xml", - "lib/portable-net45+win8+wpa81/System.Diagnostics.DiagnosticSource.dll", - "lib/portable-net45+win8+wpa81/System.Diagnostics.DiagnosticSource.xml", - "system.diagnostics.diagnosticsource.4.5.0.nupkg.sha512", - "system.diagnostics.diagnosticsource.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Diagnostics.FileVersionInfo/4.3.0": { - "sha512": "CqxgtWsRjBv6WqZJKg0y3OGaU1DmHYdZNnzucE4F01kToRN3K5gnenbtgG1fV1gDN29cDGgQqAHiVmITxE26AQ==", - "type": "package", - "path": "system.diagnostics.fileversioninfo/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Diagnostics.FileVersionInfo.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Diagnostics.FileVersionInfo.dll", - "ref/netstandard1.3/System.Diagnostics.FileVersionInfo.dll", - "ref/netstandard1.3/System.Diagnostics.FileVersionInfo.xml", - "ref/netstandard1.3/de/System.Diagnostics.FileVersionInfo.xml", - "ref/netstandard1.3/es/System.Diagnostics.FileVersionInfo.xml", - "ref/netstandard1.3/fr/System.Diagnostics.FileVersionInfo.xml", - "ref/netstandard1.3/it/System.Diagnostics.FileVersionInfo.xml", - "ref/netstandard1.3/ja/System.Diagnostics.FileVersionInfo.xml", - "ref/netstandard1.3/ko/System.Diagnostics.FileVersionInfo.xml", - "ref/netstandard1.3/ru/System.Diagnostics.FileVersionInfo.xml", - "ref/netstandard1.3/zh-hans/System.Diagnostics.FileVersionInfo.xml", - "ref/netstandard1.3/zh-hant/System.Diagnostics.FileVersionInfo.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Diagnostics.FileVersionInfo.dll", - "runtimes/win/lib/net46/System.Diagnostics.FileVersionInfo.dll", - "runtimes/win/lib/netcore50/System.Diagnostics.FileVersionInfo.dll", - "runtimes/win/lib/netstandard1.3/System.Diagnostics.FileVersionInfo.dll", - "system.diagnostics.fileversioninfo.4.3.0.nupkg.sha512", - "system.diagnostics.fileversioninfo.nuspec" - ] - }, - "System.Diagnostics.Process/4.1.0": { - "sha512": "mpVZ5bnlSs3tTeJ6jYyDJEIa6tavhAd88lxq1zbYhkkCu0Pno2+gHXcvZcoygq2d8JxW3gojXqNJMTAshduqZA==", - "type": "package", - "path": "system.diagnostics.process/4.1.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Diagnostics.Process.dll", - "lib/net461/System.Diagnostics.Process.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Diagnostics.Process.dll", - "ref/net461/System.Diagnostics.Process.dll", - "ref/netstandard1.3/System.Diagnostics.Process.dll", - "ref/netstandard1.3/System.Diagnostics.Process.xml", - "ref/netstandard1.3/de/System.Diagnostics.Process.xml", - "ref/netstandard1.3/es/System.Diagnostics.Process.xml", - "ref/netstandard1.3/fr/System.Diagnostics.Process.xml", - "ref/netstandard1.3/it/System.Diagnostics.Process.xml", - "ref/netstandard1.3/ja/System.Diagnostics.Process.xml", - "ref/netstandard1.3/ko/System.Diagnostics.Process.xml", - "ref/netstandard1.3/ru/System.Diagnostics.Process.xml", - "ref/netstandard1.3/zh-hans/System.Diagnostics.Process.xml", - "ref/netstandard1.3/zh-hant/System.Diagnostics.Process.xml", - "ref/netstandard1.4/System.Diagnostics.Process.dll", - "ref/netstandard1.4/System.Diagnostics.Process.xml", - "ref/netstandard1.4/de/System.Diagnostics.Process.xml", - "ref/netstandard1.4/es/System.Diagnostics.Process.xml", - "ref/netstandard1.4/fr/System.Diagnostics.Process.xml", - "ref/netstandard1.4/it/System.Diagnostics.Process.xml", - "ref/netstandard1.4/ja/System.Diagnostics.Process.xml", - "ref/netstandard1.4/ko/System.Diagnostics.Process.xml", - "ref/netstandard1.4/ru/System.Diagnostics.Process.xml", - "ref/netstandard1.4/zh-hans/System.Diagnostics.Process.xml", - "ref/netstandard1.4/zh-hant/System.Diagnostics.Process.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/linux/lib/netstandard1.4/System.Diagnostics.Process.dll", - "runtimes/osx/lib/netstandard1.4/System.Diagnostics.Process.dll", - "runtimes/win/lib/net46/System.Diagnostics.Process.dll", - "runtimes/win/lib/net461/System.Diagnostics.Process.dll", - "runtimes/win/lib/netstandard1.4/System.Diagnostics.Process.dll", - "runtimes/win7/lib/netcore50/_._", - "system.diagnostics.process.4.1.0.nupkg.sha512", - "system.diagnostics.process.nuspec" - ] - }, - "System.Diagnostics.StackTrace/4.3.0": { - "sha512": "On2V/V1k2LSQwS1+kMIrLUdsJay3ohG5IFYm1qkALFEHqsGo79CCFxgUc+CS5qveFc+ys1zO6G4YvRu3/tLL6A==", - "type": "package", - "path": "system.diagnostics.stacktrace/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Diagnostics.StackTrace.dll", - "lib/netstandard1.3/System.Diagnostics.StackTrace.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Diagnostics.StackTrace.dll", - "ref/netstandard1.3/System.Diagnostics.StackTrace.dll", - "ref/netstandard1.3/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/de/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/es/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/fr/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/it/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/ja/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/ko/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/ru/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/zh-hans/System.Diagnostics.StackTrace.xml", - "ref/netstandard1.3/zh-hant/System.Diagnostics.StackTrace.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Diagnostics.StackTrace.dll", - "system.diagnostics.stacktrace.4.3.0.nupkg.sha512", - "system.diagnostics.stacktrace.nuspec" - ] - }, - "System.Diagnostics.Tools/4.3.0": { - "sha512": "Fk1pd+chy860Tt57/XWwO42XceBCau+l1Axxhn6WQJL9xqaAi8vFVZ7XPsLFMsplfWR2r3mknKOth5uDZvE9kA==", - "type": "package", - "path": "system.diagnostics.tools/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Diagnostics.Tools.dll", - "ref/netcore50/System.Diagnostics.Tools.xml", - "ref/netcore50/de/System.Diagnostics.Tools.xml", - "ref/netcore50/es/System.Diagnostics.Tools.xml", - "ref/netcore50/fr/System.Diagnostics.Tools.xml", - "ref/netcore50/it/System.Diagnostics.Tools.xml", - "ref/netcore50/ja/System.Diagnostics.Tools.xml", - "ref/netcore50/ko/System.Diagnostics.Tools.xml", - "ref/netcore50/ru/System.Diagnostics.Tools.xml", - "ref/netcore50/zh-hans/System.Diagnostics.Tools.xml", - "ref/netcore50/zh-hant/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/System.Diagnostics.Tools.dll", - "ref/netstandard1.0/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/de/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/es/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/fr/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/it/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/ja/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/ko/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/ru/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/zh-hans/System.Diagnostics.Tools.xml", - "ref/netstandard1.0/zh-hant/System.Diagnostics.Tools.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.diagnostics.tools.4.3.0.nupkg.sha512", - "system.diagnostics.tools.nuspec" - ] - }, - "System.Diagnostics.Tracing/4.3.0": { - "sha512": "0KXTDiYc1Ft9+rArf/vXa2TgybiS7YJuphSByYPAIIsFtpmBzXnpHNTlgR4c1MPOoGoa/OBYEezli+XkwgFp6g==", - "type": "package", - "path": "system.diagnostics.tracing/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Diagnostics.Tracing.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Diagnostics.Tracing.dll", - "ref/netcore50/System.Diagnostics.Tracing.dll", - "ref/netcore50/System.Diagnostics.Tracing.xml", - "ref/netcore50/de/System.Diagnostics.Tracing.xml", - "ref/netcore50/es/System.Diagnostics.Tracing.xml", - "ref/netcore50/fr/System.Diagnostics.Tracing.xml", - "ref/netcore50/it/System.Diagnostics.Tracing.xml", - "ref/netcore50/ja/System.Diagnostics.Tracing.xml", - "ref/netcore50/ko/System.Diagnostics.Tracing.xml", - "ref/netcore50/ru/System.Diagnostics.Tracing.xml", - "ref/netcore50/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netcore50/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/System.Diagnostics.Tracing.dll", - "ref/netstandard1.1/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.1/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/System.Diagnostics.Tracing.dll", - "ref/netstandard1.2/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.2/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/System.Diagnostics.Tracing.dll", - "ref/netstandard1.3/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.3/zh-hant/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/System.Diagnostics.Tracing.dll", - "ref/netstandard1.5/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/de/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/es/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/fr/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/it/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/ja/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/ko/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/ru/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/zh-hans/System.Diagnostics.Tracing.xml", - "ref/netstandard1.5/zh-hant/System.Diagnostics.Tracing.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.diagnostics.tracing.4.3.0.nupkg.sha512", - "system.diagnostics.tracing.nuspec" - ] - }, - "System.Dynamic.Runtime/4.3.0": { - "sha512": "VERv7pT0MsuP047BDJKah7MHp28VKi6doRupnEHOsPZZE88hiUSZDw4SLU+FiUUJHpgGyEwCha2h/Mk5M30w6g==", - "type": "package", - "path": "system.dynamic.runtime/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Dynamic.Runtime.dll", - "lib/netstandard1.3/System.Dynamic.Runtime.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Dynamic.Runtime.dll", - "ref/netcore50/System.Dynamic.Runtime.xml", - "ref/netcore50/de/System.Dynamic.Runtime.xml", - "ref/netcore50/es/System.Dynamic.Runtime.xml", - "ref/netcore50/fr/System.Dynamic.Runtime.xml", - "ref/netcore50/it/System.Dynamic.Runtime.xml", - "ref/netcore50/ja/System.Dynamic.Runtime.xml", - "ref/netcore50/ko/System.Dynamic.Runtime.xml", - "ref/netcore50/ru/System.Dynamic.Runtime.xml", - "ref/netcore50/zh-hans/System.Dynamic.Runtime.xml", - "ref/netcore50/zh-hant/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/System.Dynamic.Runtime.dll", - "ref/netstandard1.0/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/de/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/es/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/fr/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/it/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/ja/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/ko/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/ru/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/zh-hans/System.Dynamic.Runtime.xml", - "ref/netstandard1.0/zh-hant/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/System.Dynamic.Runtime.dll", - "ref/netstandard1.3/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/de/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/es/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/fr/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/it/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/ja/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/ko/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/ru/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/zh-hans/System.Dynamic.Runtime.xml", - "ref/netstandard1.3/zh-hant/System.Dynamic.Runtime.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Dynamic.Runtime.dll", - "system.dynamic.runtime.4.3.0.nupkg.sha512", - "system.dynamic.runtime.nuspec" - ] - }, - "System.Globalization/4.3.0": { - "sha512": "gj0rowjLBztAoxRuzM0Nn9exYVrD+++xb3PYc+QR/YHDvch98gbT3H4vFMnNU6r8poSjVwwlRxKAqtqN6AXs4g==", - "type": "package", - "path": "system.globalization/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Globalization.dll", - "ref/netcore50/System.Globalization.xml", - "ref/netcore50/de/System.Globalization.xml", - "ref/netcore50/es/System.Globalization.xml", - "ref/netcore50/fr/System.Globalization.xml", - "ref/netcore50/it/System.Globalization.xml", - "ref/netcore50/ja/System.Globalization.xml", - "ref/netcore50/ko/System.Globalization.xml", - "ref/netcore50/ru/System.Globalization.xml", - "ref/netcore50/zh-hans/System.Globalization.xml", - "ref/netcore50/zh-hant/System.Globalization.xml", - "ref/netstandard1.0/System.Globalization.dll", - "ref/netstandard1.0/System.Globalization.xml", - "ref/netstandard1.0/de/System.Globalization.xml", - "ref/netstandard1.0/es/System.Globalization.xml", - "ref/netstandard1.0/fr/System.Globalization.xml", - "ref/netstandard1.0/it/System.Globalization.xml", - "ref/netstandard1.0/ja/System.Globalization.xml", - "ref/netstandard1.0/ko/System.Globalization.xml", - "ref/netstandard1.0/ru/System.Globalization.xml", - "ref/netstandard1.0/zh-hans/System.Globalization.xml", - "ref/netstandard1.0/zh-hant/System.Globalization.xml", - "ref/netstandard1.3/System.Globalization.dll", - "ref/netstandard1.3/System.Globalization.xml", - "ref/netstandard1.3/de/System.Globalization.xml", - "ref/netstandard1.3/es/System.Globalization.xml", - "ref/netstandard1.3/fr/System.Globalization.xml", - "ref/netstandard1.3/it/System.Globalization.xml", - "ref/netstandard1.3/ja/System.Globalization.xml", - "ref/netstandard1.3/ko/System.Globalization.xml", - "ref/netstandard1.3/ru/System.Globalization.xml", - "ref/netstandard1.3/zh-hans/System.Globalization.xml", - "ref/netstandard1.3/zh-hant/System.Globalization.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.globalization.4.3.0.nupkg.sha512", - "system.globalization.nuspec" - ] - }, - "System.Globalization.Calendars/4.3.0": { - "sha512": "6XGQIxQCs5N3S5Je/AKiv6QdHRF6F/uH2m45n1I0VGlidn6c2POZcO+kCOT0U80eZ1Giph42a8l0BuGwuKS+hg==", - "type": "package", - "path": "system.globalization.calendars/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Globalization.Calendars.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Globalization.Calendars.dll", - "ref/netstandard1.3/System.Globalization.Calendars.dll", - "ref/netstandard1.3/System.Globalization.Calendars.xml", - "ref/netstandard1.3/de/System.Globalization.Calendars.xml", - "ref/netstandard1.3/es/System.Globalization.Calendars.xml", - "ref/netstandard1.3/fr/System.Globalization.Calendars.xml", - "ref/netstandard1.3/it/System.Globalization.Calendars.xml", - "ref/netstandard1.3/ja/System.Globalization.Calendars.xml", - "ref/netstandard1.3/ko/System.Globalization.Calendars.xml", - "ref/netstandard1.3/ru/System.Globalization.Calendars.xml", - "ref/netstandard1.3/zh-hans/System.Globalization.Calendars.xml", - "ref/netstandard1.3/zh-hant/System.Globalization.Calendars.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.globalization.calendars.4.3.0.nupkg.sha512", - "system.globalization.calendars.nuspec" - ] - }, - "System.Globalization.Extensions/4.3.0": { - "sha512": "pNNgAD+V4MMe3znAuR4cc4UKYKxdADKxfbiIo8fXE0zvms2XIZ0UF0rSE7fARPSbNkzFcgBz6/y24b9uTsJM5Q==", - "type": "package", - "path": "system.globalization.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Globalization.Extensions.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Globalization.Extensions.dll", - "ref/netstandard1.3/System.Globalization.Extensions.dll", - "ref/netstandard1.3/System.Globalization.Extensions.xml", - "ref/netstandard1.3/de/System.Globalization.Extensions.xml", - "ref/netstandard1.3/es/System.Globalization.Extensions.xml", - "ref/netstandard1.3/fr/System.Globalization.Extensions.xml", - "ref/netstandard1.3/it/System.Globalization.Extensions.xml", - "ref/netstandard1.3/ja/System.Globalization.Extensions.xml", - "ref/netstandard1.3/ko/System.Globalization.Extensions.xml", - "ref/netstandard1.3/ru/System.Globalization.Extensions.xml", - "ref/netstandard1.3/zh-hans/System.Globalization.Extensions.xml", - "ref/netstandard1.3/zh-hant/System.Globalization.Extensions.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Globalization.Extensions.dll", - "runtimes/win/lib/net46/System.Globalization.Extensions.dll", - "runtimes/win/lib/netstandard1.3/System.Globalization.Extensions.dll", - "system.globalization.extensions.4.3.0.nupkg.sha512", - "system.globalization.extensions.nuspec" - ] - }, - "System.IdentityModel.Tokens.Jwt/5.2.0": { - "sha512": "VnyZBEbFfmIm9Ti3wKCkwcUuL9exJ/Zo0fkapCBvJwPX9H+YirZGVjLkRGbZTEXMKhvDQpN6lbtN9skDR3h14A==", - "type": "package", - "path": "system.identitymodel.tokens.jwt/5.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/System.IdentityModel.Tokens.Jwt.dll", - "lib/net45/System.IdentityModel.Tokens.Jwt.xml", - "lib/net451/System.IdentityModel.Tokens.Jwt.dll", - "lib/net451/System.IdentityModel.Tokens.Jwt.xml", - "lib/netstandard1.4/System.IdentityModel.Tokens.Jwt.dll", - "lib/netstandard1.4/System.IdentityModel.Tokens.Jwt.xml", - "system.identitymodel.tokens.jwt.5.2.0.nupkg.sha512", - "system.identitymodel.tokens.jwt.nuspec" - ] - }, - "System.Interactive.Async/3.1.1": { - "sha512": "hZccYiIE5RS1/J9Tb/BNtosAGVggdlsJm4Ojdu+gDV0p4AIi+LUfUogMKkRacljQEJd2AG6vYzvcjhQFkqoZmw==", - "type": "package", - "path": "system.interactive.async/3.1.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net45/System.Interactive.Async.dll", - "lib/net45/System.Interactive.Async.xml", - "lib/net46/System.Interactive.Async.dll", - "lib/net46/System.Interactive.Async.xml", - "lib/netstandard1.0/System.Interactive.Async.dll", - "lib/netstandard1.0/System.Interactive.Async.xml", - "lib/netstandard1.3/System.Interactive.Async.dll", - "lib/netstandard1.3/System.Interactive.Async.xml", - "system.interactive.async.3.1.1.nupkg.sha512", - "system.interactive.async.nuspec" - ] - }, - "System.IO/4.3.0": { - "sha512": "v8paIePhmGuXZbE9xvvNb4uJ5ME4OFXR1+8la/G/L1GIl2nbU2WFnddgb79kVK3U2us7q1aZT/uY/R0D/ovB5g==", - "type": "package", - "path": "system.io/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.IO.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.IO.dll", - "ref/netcore50/System.IO.dll", - "ref/netcore50/System.IO.xml", - "ref/netcore50/de/System.IO.xml", - "ref/netcore50/es/System.IO.xml", - "ref/netcore50/fr/System.IO.xml", - "ref/netcore50/it/System.IO.xml", - "ref/netcore50/ja/System.IO.xml", - "ref/netcore50/ko/System.IO.xml", - "ref/netcore50/ru/System.IO.xml", - "ref/netcore50/zh-hans/System.IO.xml", - "ref/netcore50/zh-hant/System.IO.xml", - "ref/netstandard1.0/System.IO.dll", - "ref/netstandard1.0/System.IO.xml", - "ref/netstandard1.0/de/System.IO.xml", - "ref/netstandard1.0/es/System.IO.xml", - "ref/netstandard1.0/fr/System.IO.xml", - "ref/netstandard1.0/it/System.IO.xml", - "ref/netstandard1.0/ja/System.IO.xml", - "ref/netstandard1.0/ko/System.IO.xml", - "ref/netstandard1.0/ru/System.IO.xml", - "ref/netstandard1.0/zh-hans/System.IO.xml", - "ref/netstandard1.0/zh-hant/System.IO.xml", - "ref/netstandard1.3/System.IO.dll", - "ref/netstandard1.3/System.IO.xml", - "ref/netstandard1.3/de/System.IO.xml", - "ref/netstandard1.3/es/System.IO.xml", - "ref/netstandard1.3/fr/System.IO.xml", - "ref/netstandard1.3/it/System.IO.xml", - "ref/netstandard1.3/ja/System.IO.xml", - "ref/netstandard1.3/ko/System.IO.xml", - "ref/netstandard1.3/ru/System.IO.xml", - "ref/netstandard1.3/zh-hans/System.IO.xml", - "ref/netstandard1.3/zh-hant/System.IO.xml", - "ref/netstandard1.5/System.IO.dll", - "ref/netstandard1.5/System.IO.xml", - "ref/netstandard1.5/de/System.IO.xml", - "ref/netstandard1.5/es/System.IO.xml", - "ref/netstandard1.5/fr/System.IO.xml", - "ref/netstandard1.5/it/System.IO.xml", - "ref/netstandard1.5/ja/System.IO.xml", - "ref/netstandard1.5/ko/System.IO.xml", - "ref/netstandard1.5/ru/System.IO.xml", - "ref/netstandard1.5/zh-hans/System.IO.xml", - "ref/netstandard1.5/zh-hant/System.IO.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.4.3.0.nupkg.sha512", - "system.io.nuspec" - ] - }, - "System.IO.Abstractions/6.0.3": { - "sha512": "sGph5mBx7iH7Eab0J9teJ0c+dC7HYIbjgCRzmCM8MIBTaKoHpd9Asp74AAPu/uxMciNjXBj2bY4rxIVXzekBRQ==", - "type": "package", - "path": "system.io.abstractions/6.0.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net40/System.IO.Abstractions.dll", - "lib/net40/System.IO.Abstractions.xml", - "lib/netcoreapp2.0/System.IO.Abstractions.dll", - "lib/netcoreapp2.0/System.IO.Abstractions.xml", - "lib/netstandard1.4/System.IO.Abstractions.dll", - "lib/netstandard1.4/System.IO.Abstractions.xml", - "lib/netstandard2.0/System.IO.Abstractions.dll", - "lib/netstandard2.0/System.IO.Abstractions.xml", - "system.io.abstractions.6.0.3.nupkg.sha512", - "system.io.abstractions.nuspec" - ] - }, - "System.IO.Compression/4.3.0": { - "sha512": "9UDuUaO7aUHN+6rOmpc41/eYai+Udw22H0Wojst+82tXHUwHQX3InNvpZVomK3zFmbCkt47/6pGBnhhhIbRIBw==", - "type": "package", - "path": "system.io.compression/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.IO.Compression.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.IO.Compression.dll", - "ref/netcore50/System.IO.Compression.dll", - "ref/netcore50/System.IO.Compression.xml", - "ref/netcore50/de/System.IO.Compression.xml", - "ref/netcore50/es/System.IO.Compression.xml", - "ref/netcore50/fr/System.IO.Compression.xml", - "ref/netcore50/it/System.IO.Compression.xml", - "ref/netcore50/ja/System.IO.Compression.xml", - "ref/netcore50/ko/System.IO.Compression.xml", - "ref/netcore50/ru/System.IO.Compression.xml", - "ref/netcore50/zh-hans/System.IO.Compression.xml", - "ref/netcore50/zh-hant/System.IO.Compression.xml", - "ref/netstandard1.1/System.IO.Compression.dll", - "ref/netstandard1.1/System.IO.Compression.xml", - "ref/netstandard1.1/de/System.IO.Compression.xml", - "ref/netstandard1.1/es/System.IO.Compression.xml", - "ref/netstandard1.1/fr/System.IO.Compression.xml", - "ref/netstandard1.1/it/System.IO.Compression.xml", - "ref/netstandard1.1/ja/System.IO.Compression.xml", - "ref/netstandard1.1/ko/System.IO.Compression.xml", - "ref/netstandard1.1/ru/System.IO.Compression.xml", - "ref/netstandard1.1/zh-hans/System.IO.Compression.xml", - "ref/netstandard1.1/zh-hant/System.IO.Compression.xml", - "ref/netstandard1.3/System.IO.Compression.dll", - "ref/netstandard1.3/System.IO.Compression.xml", - "ref/netstandard1.3/de/System.IO.Compression.xml", - "ref/netstandard1.3/es/System.IO.Compression.xml", - "ref/netstandard1.3/fr/System.IO.Compression.xml", - "ref/netstandard1.3/it/System.IO.Compression.xml", - "ref/netstandard1.3/ja/System.IO.Compression.xml", - "ref/netstandard1.3/ko/System.IO.Compression.xml", - "ref/netstandard1.3/ru/System.IO.Compression.xml", - "ref/netstandard1.3/zh-hans/System.IO.Compression.xml", - "ref/netstandard1.3/zh-hant/System.IO.Compression.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.IO.Compression.dll", - "runtimes/win/lib/net46/System.IO.Compression.dll", - "runtimes/win/lib/netstandard1.3/System.IO.Compression.dll", - "system.io.compression.4.3.0.nupkg.sha512", - "system.io.compression.nuspec" - ] - }, - "System.IO.Compression.ZipFile/4.3.0": { - "sha512": "GGBjRnJ2f4GPAZLsKydQaT8NOTkPO31ADMb9T250pcvtJ79J5ZgOyF/z4WHDD2GQ9wDjOaEEDBaZuH60qntnkg==", - "type": "package", - "path": "system.io.compression.zipfile/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.IO.Compression.ZipFile.dll", - "lib/netstandard1.3/System.IO.Compression.ZipFile.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.IO.Compression.ZipFile.dll", - "ref/netstandard1.3/System.IO.Compression.ZipFile.dll", - "ref/netstandard1.3/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/de/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/es/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/fr/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/it/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/ja/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/ko/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/ru/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/zh-hans/System.IO.Compression.ZipFile.xml", - "ref/netstandard1.3/zh-hant/System.IO.Compression.ZipFile.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.compression.zipfile.4.3.0.nupkg.sha512", - "system.io.compression.zipfile.nuspec" - ] - }, - "System.IO.FileSystem/4.3.0": { - "sha512": "T7WB1vhblSmgkaDpdGM3Uqo55Qsr5sip5eyowrwiXOoHBkzOx3ePd9+Zh97r9NzOwFCxqX7awO6RBxQuao7n7g==", - "type": "package", - "path": "system.io.filesystem/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.IO.FileSystem.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.IO.FileSystem.dll", - "ref/netstandard1.3/System.IO.FileSystem.dll", - "ref/netstandard1.3/System.IO.FileSystem.xml", - "ref/netstandard1.3/de/System.IO.FileSystem.xml", - "ref/netstandard1.3/es/System.IO.FileSystem.xml", - "ref/netstandard1.3/fr/System.IO.FileSystem.xml", - "ref/netstandard1.3/it/System.IO.FileSystem.xml", - "ref/netstandard1.3/ja/System.IO.FileSystem.xml", - "ref/netstandard1.3/ko/System.IO.FileSystem.xml", - "ref/netstandard1.3/ru/System.IO.FileSystem.xml", - "ref/netstandard1.3/zh-hans/System.IO.FileSystem.xml", - "ref/netstandard1.3/zh-hant/System.IO.FileSystem.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.filesystem.4.3.0.nupkg.sha512", - "system.io.filesystem.nuspec" - ] - }, - "System.IO.FileSystem.AccessControl/4.5.0": { - "sha512": "TYe6xstoqT5MlTly0OtPU6u9zWuNScLVMEx6sTCjjx+Hqdp0wCXoG6fnzMpTPMQACXQzi9pd2N5Tloow+5jQdQ==", - "type": "package", - "path": "system.io.filesystem.accesscontrol/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net46/System.IO.FileSystem.AccessControl.dll", - "lib/net461/System.IO.FileSystem.AccessControl.dll", - "lib/netstandard1.3/System.IO.FileSystem.AccessControl.dll", - "lib/netstandard2.0/System.IO.FileSystem.AccessControl.dll", - "ref/net46/System.IO.FileSystem.AccessControl.dll", - "ref/net461/System.IO.FileSystem.AccessControl.dll", - "ref/net461/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/System.IO.FileSystem.AccessControl.dll", - "ref/netstandard1.3/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/de/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/es/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/fr/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/it/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/ja/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/ko/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/ru/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/zh-hans/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard1.3/zh-hant/System.IO.FileSystem.AccessControl.xml", - "ref/netstandard2.0/System.IO.FileSystem.AccessControl.dll", - "ref/netstandard2.0/System.IO.FileSystem.AccessControl.xml", - "runtimes/win/lib/net46/System.IO.FileSystem.AccessControl.dll", - "runtimes/win/lib/net461/System.IO.FileSystem.AccessControl.dll", - "runtimes/win/lib/netstandard1.3/System.IO.FileSystem.AccessControl.dll", - "runtimes/win/lib/netstandard2.0/System.IO.FileSystem.AccessControl.dll", - "system.io.filesystem.accesscontrol.4.5.0.nupkg.sha512", - "system.io.filesystem.accesscontrol.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.IO.FileSystem.Primitives/4.3.0": { - "sha512": "WIWVPQlYLP/Zc9I6IakpBk1y8ryVGK83MtZx//zGKKi2hvHQWKAB7moRQCOz5Is/wNDksiYpocf3FeA3le6e5Q==", - "type": "package", - "path": "system.io.filesystem.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.IO.FileSystem.Primitives.dll", - "lib/netstandard1.3/System.IO.FileSystem.Primitives.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.IO.FileSystem.Primitives.dll", - "ref/netstandard1.3/System.IO.FileSystem.Primitives.dll", - "ref/netstandard1.3/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/de/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/es/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/fr/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/it/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/ja/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/ko/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/ru/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/zh-hans/System.IO.FileSystem.Primitives.xml", - "ref/netstandard1.3/zh-hant/System.IO.FileSystem.Primitives.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.io.filesystem.primitives.4.3.0.nupkg.sha512", - "system.io.filesystem.primitives.nuspec" - ] - }, - "System.IO.Pipelines/4.5.2": { - "sha512": "w+rt22tsDE5mwCZ+erMQMXfkoGwJL1RWlZxq49WD2/tLDnzHjoV5b/a3ns0tJdtP+SyR7JJO+6IFe8EWOHjTIg==", - "type": "package", - "path": "system.io.pipelines/4.5.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netcoreapp2.1/System.IO.Pipelines.dll", - "lib/netcoreapp2.1/System.IO.Pipelines.xml", - "lib/netstandard1.3/System.IO.Pipelines.dll", - "lib/netstandard1.3/System.IO.Pipelines.xml", - "lib/netstandard2.0/System.IO.Pipelines.dll", - "lib/netstandard2.0/System.IO.Pipelines.xml", - "ref/netstandard1.3/System.IO.Pipelines.dll", - "system.io.pipelines.4.5.2.nupkg.sha512", - "system.io.pipelines.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Linq/4.3.0": { - "sha512": "6sx/4exSb0BfW6DmcfYW0OW+nBgo1UOp4vjGXfQJnWsupKn6LNrk80sXDcNxQvYOJn4TfKOfNQKB7XDS3GIEWA==", - "type": "package", - "path": "system.linq/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net463/System.Linq.dll", - "lib/netcore50/System.Linq.dll", - "lib/netstandard1.6/System.Linq.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net463/System.Linq.dll", - "ref/netcore50/System.Linq.dll", - "ref/netcore50/System.Linq.xml", - "ref/netcore50/de/System.Linq.xml", - "ref/netcore50/es/System.Linq.xml", - "ref/netcore50/fr/System.Linq.xml", - "ref/netcore50/it/System.Linq.xml", - "ref/netcore50/ja/System.Linq.xml", - "ref/netcore50/ko/System.Linq.xml", - "ref/netcore50/ru/System.Linq.xml", - "ref/netcore50/zh-hans/System.Linq.xml", - "ref/netcore50/zh-hant/System.Linq.xml", - "ref/netstandard1.0/System.Linq.dll", - "ref/netstandard1.0/System.Linq.xml", - "ref/netstandard1.0/de/System.Linq.xml", - "ref/netstandard1.0/es/System.Linq.xml", - "ref/netstandard1.0/fr/System.Linq.xml", - "ref/netstandard1.0/it/System.Linq.xml", - "ref/netstandard1.0/ja/System.Linq.xml", - "ref/netstandard1.0/ko/System.Linq.xml", - "ref/netstandard1.0/ru/System.Linq.xml", - "ref/netstandard1.0/zh-hans/System.Linq.xml", - "ref/netstandard1.0/zh-hant/System.Linq.xml", - "ref/netstandard1.6/System.Linq.dll", - "ref/netstandard1.6/System.Linq.xml", - "ref/netstandard1.6/de/System.Linq.xml", - "ref/netstandard1.6/es/System.Linq.xml", - "ref/netstandard1.6/fr/System.Linq.xml", - "ref/netstandard1.6/it/System.Linq.xml", - "ref/netstandard1.6/ja/System.Linq.xml", - "ref/netstandard1.6/ko/System.Linq.xml", - "ref/netstandard1.6/ru/System.Linq.xml", - "ref/netstandard1.6/zh-hans/System.Linq.xml", - "ref/netstandard1.6/zh-hant/System.Linq.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.linq.4.3.0.nupkg.sha512", - "system.linq.nuspec" - ] - }, - "System.Linq.Expressions/4.3.0": { - "sha512": "YbkO+a5vd5+8intkg+6PVEnN0FyBsFI19wRH5lanOyqrfDQXhLmZ91MjdHRKcuLDpc0TgA6iNBf6wyzPrlzebQ==", - "type": "package", - "path": "system.linq.expressions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net463/System.Linq.Expressions.dll", - "lib/netcore50/System.Linq.Expressions.dll", - "lib/netstandard1.6/System.Linq.Expressions.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net463/System.Linq.Expressions.dll", - "ref/netcore50/System.Linq.Expressions.dll", - "ref/netcore50/System.Linq.Expressions.xml", - "ref/netcore50/de/System.Linq.Expressions.xml", - "ref/netcore50/es/System.Linq.Expressions.xml", - "ref/netcore50/fr/System.Linq.Expressions.xml", - "ref/netcore50/it/System.Linq.Expressions.xml", - "ref/netcore50/ja/System.Linq.Expressions.xml", - "ref/netcore50/ko/System.Linq.Expressions.xml", - "ref/netcore50/ru/System.Linq.Expressions.xml", - "ref/netcore50/zh-hans/System.Linq.Expressions.xml", - "ref/netcore50/zh-hant/System.Linq.Expressions.xml", - "ref/netstandard1.0/System.Linq.Expressions.dll", - "ref/netstandard1.0/System.Linq.Expressions.xml", - "ref/netstandard1.0/de/System.Linq.Expressions.xml", - "ref/netstandard1.0/es/System.Linq.Expressions.xml", - "ref/netstandard1.0/fr/System.Linq.Expressions.xml", - "ref/netstandard1.0/it/System.Linq.Expressions.xml", - "ref/netstandard1.0/ja/System.Linq.Expressions.xml", - "ref/netstandard1.0/ko/System.Linq.Expressions.xml", - "ref/netstandard1.0/ru/System.Linq.Expressions.xml", - "ref/netstandard1.0/zh-hans/System.Linq.Expressions.xml", - "ref/netstandard1.0/zh-hant/System.Linq.Expressions.xml", - "ref/netstandard1.3/System.Linq.Expressions.dll", - "ref/netstandard1.3/System.Linq.Expressions.xml", - "ref/netstandard1.3/de/System.Linq.Expressions.xml", - "ref/netstandard1.3/es/System.Linq.Expressions.xml", - "ref/netstandard1.3/fr/System.Linq.Expressions.xml", - "ref/netstandard1.3/it/System.Linq.Expressions.xml", - "ref/netstandard1.3/ja/System.Linq.Expressions.xml", - "ref/netstandard1.3/ko/System.Linq.Expressions.xml", - "ref/netstandard1.3/ru/System.Linq.Expressions.xml", - "ref/netstandard1.3/zh-hans/System.Linq.Expressions.xml", - "ref/netstandard1.3/zh-hant/System.Linq.Expressions.xml", - "ref/netstandard1.6/System.Linq.Expressions.dll", - "ref/netstandard1.6/System.Linq.Expressions.xml", - "ref/netstandard1.6/de/System.Linq.Expressions.xml", - "ref/netstandard1.6/es/System.Linq.Expressions.xml", - "ref/netstandard1.6/fr/System.Linq.Expressions.xml", - "ref/netstandard1.6/it/System.Linq.Expressions.xml", - "ref/netstandard1.6/ja/System.Linq.Expressions.xml", - "ref/netstandard1.6/ko/System.Linq.Expressions.xml", - "ref/netstandard1.6/ru/System.Linq.Expressions.xml", - "ref/netstandard1.6/zh-hans/System.Linq.Expressions.xml", - "ref/netstandard1.6/zh-hant/System.Linq.Expressions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Linq.Expressions.dll", - "system.linq.expressions.4.3.0.nupkg.sha512", - "system.linq.expressions.nuspec" - ] - }, - "System.Linq.Queryable/4.3.0": { - "sha512": "In1Bmmvl/j52yPu3xgakQSI0YIckPUr870w4K5+Lak3JCCa8hl+my65lABOuKfYs4ugmZy25ScFerC4nz8+b6g==", - "type": "package", - "path": "system.linq.queryable/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/monoandroid10/_._", - "lib/monotouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Linq.Queryable.dll", - "lib/netstandard1.3/System.Linq.Queryable.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/monoandroid10/_._", - "ref/monotouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Linq.Queryable.dll", - "ref/netcore50/System.Linq.Queryable.xml", - "ref/netcore50/de/System.Linq.Queryable.xml", - "ref/netcore50/es/System.Linq.Queryable.xml", - "ref/netcore50/fr/System.Linq.Queryable.xml", - "ref/netcore50/it/System.Linq.Queryable.xml", - "ref/netcore50/ja/System.Linq.Queryable.xml", - "ref/netcore50/ko/System.Linq.Queryable.xml", - "ref/netcore50/ru/System.Linq.Queryable.xml", - "ref/netcore50/zh-hans/System.Linq.Queryable.xml", - "ref/netcore50/zh-hant/System.Linq.Queryable.xml", - "ref/netstandard1.0/System.Linq.Queryable.dll", - "ref/netstandard1.0/System.Linq.Queryable.xml", - "ref/netstandard1.0/de/System.Linq.Queryable.xml", - "ref/netstandard1.0/es/System.Linq.Queryable.xml", - "ref/netstandard1.0/fr/System.Linq.Queryable.xml", - "ref/netstandard1.0/it/System.Linq.Queryable.xml", - "ref/netstandard1.0/ja/System.Linq.Queryable.xml", - "ref/netstandard1.0/ko/System.Linq.Queryable.xml", - "ref/netstandard1.0/ru/System.Linq.Queryable.xml", - "ref/netstandard1.0/zh-hans/System.Linq.Queryable.xml", - "ref/netstandard1.0/zh-hant/System.Linq.Queryable.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.linq.queryable.4.3.0.nupkg.sha512", - "system.linq.queryable.nuspec" - ] - }, - "System.Memory/4.5.2": { - "sha512": "fvq1GNmUFwbKv+aLVYYdgu/+gc8Nu9oFujOxIjPrsf+meis9JBzTPDL6aP/eeGOz9yPj6rRLUbOjKMpsMEWpNg==", - "type": "package", - "path": "system.memory/4.5.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netcoreapp2.1/_._", - "lib/netstandard1.1/System.Memory.dll", - "lib/netstandard1.1/System.Memory.xml", - "lib/netstandard2.0/System.Memory.dll", - "lib/netstandard2.0/System.Memory.xml", - "ref/netcoreapp2.1/_._", - "system.memory.4.5.2.nupkg.sha512", - "system.memory.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Net.Http/4.3.0": { - "sha512": "6BBc6BUa7pWFL7KUI/c8wb18IobTZHTtcQKksxJI5F9DRDShdtOvBEJzg5jJbFdTll7gRE+5yXUlq72ciLE+QQ==", - "type": "package", - "path": "system.net.http/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/Xamarinmac20/_._", - "lib/monoandroid10/_._", - "lib/monotouch10/_._", - "lib/net45/_._", - "lib/net46/System.Net.Http.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/Xamarinmac20/_._", - "ref/monoandroid10/_._", - "ref/monotouch10/_._", - "ref/net45/_._", - "ref/net46/System.Net.Http.dll", - "ref/net46/System.Net.Http.xml", - "ref/net46/de/System.Net.Http.xml", - "ref/net46/es/System.Net.Http.xml", - "ref/net46/fr/System.Net.Http.xml", - "ref/net46/it/System.Net.Http.xml", - "ref/net46/ja/System.Net.Http.xml", - "ref/net46/ko/System.Net.Http.xml", - "ref/net46/ru/System.Net.Http.xml", - "ref/net46/zh-hans/System.Net.Http.xml", - "ref/net46/zh-hant/System.Net.Http.xml", - "ref/netcore50/System.Net.Http.dll", - "ref/netcore50/System.Net.Http.xml", - "ref/netcore50/de/System.Net.Http.xml", - "ref/netcore50/es/System.Net.Http.xml", - "ref/netcore50/fr/System.Net.Http.xml", - "ref/netcore50/it/System.Net.Http.xml", - "ref/netcore50/ja/System.Net.Http.xml", - "ref/netcore50/ko/System.Net.Http.xml", - "ref/netcore50/ru/System.Net.Http.xml", - "ref/netcore50/zh-hans/System.Net.Http.xml", - "ref/netcore50/zh-hant/System.Net.Http.xml", - "ref/netstandard1.1/System.Net.Http.dll", - "ref/netstandard1.1/System.Net.Http.xml", - "ref/netstandard1.1/de/System.Net.Http.xml", - "ref/netstandard1.1/es/System.Net.Http.xml", - "ref/netstandard1.1/fr/System.Net.Http.xml", - "ref/netstandard1.1/it/System.Net.Http.xml", - "ref/netstandard1.1/ja/System.Net.Http.xml", - "ref/netstandard1.1/ko/System.Net.Http.xml", - "ref/netstandard1.1/ru/System.Net.Http.xml", - "ref/netstandard1.1/zh-hans/System.Net.Http.xml", - "ref/netstandard1.1/zh-hant/System.Net.Http.xml", - "ref/netstandard1.3/System.Net.Http.dll", - "ref/netstandard1.3/System.Net.Http.xml", - "ref/netstandard1.3/de/System.Net.Http.xml", - "ref/netstandard1.3/es/System.Net.Http.xml", - "ref/netstandard1.3/fr/System.Net.Http.xml", - "ref/netstandard1.3/it/System.Net.Http.xml", - "ref/netstandard1.3/ja/System.Net.Http.xml", - "ref/netstandard1.3/ko/System.Net.Http.xml", - "ref/netstandard1.3/ru/System.Net.Http.xml", - "ref/netstandard1.3/zh-hans/System.Net.Http.xml", - "ref/netstandard1.3/zh-hant/System.Net.Http.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.6/System.Net.Http.dll", - "runtimes/win/lib/net46/System.Net.Http.dll", - "runtimes/win/lib/netcore50/System.Net.Http.dll", - "runtimes/win/lib/netstandard1.3/System.Net.Http.dll", - "system.net.http.4.3.0.nupkg.sha512", - "system.net.http.nuspec" - ] - }, - "System.Net.NameResolution/4.3.0": { - "sha512": "QNOeEx/no5Lljp9YtRa124g4PekcBbdx9eUJrPRsyHQnHpBiPTJ6sDkAOrjycUFEaUOQJhJ43jJOGu4iioKKtA==", - "type": "package", - "path": "system.net.nameresolution/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Net.NameResolution.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Net.NameResolution.dll", - "ref/netstandard1.3/System.Net.NameResolution.dll", - "ref/netstandard1.3/System.Net.NameResolution.xml", - "ref/netstandard1.3/de/System.Net.NameResolution.xml", - "ref/netstandard1.3/es/System.Net.NameResolution.xml", - "ref/netstandard1.3/fr/System.Net.NameResolution.xml", - "ref/netstandard1.3/it/System.Net.NameResolution.xml", - "ref/netstandard1.3/ja/System.Net.NameResolution.xml", - "ref/netstandard1.3/ko/System.Net.NameResolution.xml", - "ref/netstandard1.3/ru/System.Net.NameResolution.xml", - "ref/netstandard1.3/zh-hans/System.Net.NameResolution.xml", - "ref/netstandard1.3/zh-hant/System.Net.NameResolution.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Net.NameResolution.dll", - "runtimes/win/lib/net46/System.Net.NameResolution.dll", - "runtimes/win/lib/netcore50/System.Net.NameResolution.dll", - "runtimes/win/lib/netstandard1.3/System.Net.NameResolution.dll", - "system.net.nameresolution.4.3.0.nupkg.sha512", - "system.net.nameresolution.nuspec" - ] - }, - "System.Net.NetworkInformation/4.3.0": { - "sha512": "zNVmWVry0pAu7lcrRBhwwU96WUdbsrGL3azyzsbXmVNptae1+Za+UgOe9Z6s8iaWhPn7/l4wQqhC56HZWq7tkg==", - "type": "package", - "path": "system.net.networkinformation/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.Net.NetworkInformation.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.Net.NetworkInformation.dll", - "ref/netcore50/System.Net.NetworkInformation.dll", - "ref/netcore50/System.Net.NetworkInformation.xml", - "ref/netcore50/de/System.Net.NetworkInformation.xml", - "ref/netcore50/es/System.Net.NetworkInformation.xml", - "ref/netcore50/fr/System.Net.NetworkInformation.xml", - "ref/netcore50/it/System.Net.NetworkInformation.xml", - "ref/netcore50/ja/System.Net.NetworkInformation.xml", - "ref/netcore50/ko/System.Net.NetworkInformation.xml", - "ref/netcore50/ru/System.Net.NetworkInformation.xml", - "ref/netcore50/zh-hans/System.Net.NetworkInformation.xml", - "ref/netcore50/zh-hant/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/System.Net.NetworkInformation.dll", - "ref/netstandard1.0/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/de/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/es/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/fr/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/it/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/ja/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/ko/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/ru/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/zh-hans/System.Net.NetworkInformation.xml", - "ref/netstandard1.0/zh-hant/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/System.Net.NetworkInformation.dll", - "ref/netstandard1.3/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/de/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/es/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/fr/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/it/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/ja/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/ko/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/ru/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/zh-hans/System.Net.NetworkInformation.xml", - "ref/netstandard1.3/zh-hant/System.Net.NetworkInformation.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/linux/lib/netstandard1.3/System.Net.NetworkInformation.dll", - "runtimes/osx/lib/netstandard1.3/System.Net.NetworkInformation.dll", - "runtimes/win/lib/net46/System.Net.NetworkInformation.dll", - "runtimes/win/lib/netcore50/System.Net.NetworkInformation.dll", - "runtimes/win/lib/netstandard1.3/System.Net.NetworkInformation.dll", - "system.net.networkinformation.4.3.0.nupkg.sha512", - "system.net.networkinformation.nuspec" - ] - }, - "System.Net.Primitives/4.3.0": { - "sha512": "n3/ezjMKgfMxLqfIBJJ4UkE77iySnzBmtzaZOAPfR8wGkvvKI2wiK/GdyPWbQvVPKkwA2ppNYk5FjaWHTRJ85g==", - "type": "package", - "path": "system.net.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Net.Primitives.dll", - "ref/netcore50/System.Net.Primitives.xml", - "ref/netcore50/de/System.Net.Primitives.xml", - "ref/netcore50/es/System.Net.Primitives.xml", - "ref/netcore50/fr/System.Net.Primitives.xml", - "ref/netcore50/it/System.Net.Primitives.xml", - "ref/netcore50/ja/System.Net.Primitives.xml", - "ref/netcore50/ko/System.Net.Primitives.xml", - "ref/netcore50/ru/System.Net.Primitives.xml", - "ref/netcore50/zh-hans/System.Net.Primitives.xml", - "ref/netcore50/zh-hant/System.Net.Primitives.xml", - "ref/netstandard1.0/System.Net.Primitives.dll", - "ref/netstandard1.0/System.Net.Primitives.xml", - "ref/netstandard1.0/de/System.Net.Primitives.xml", - "ref/netstandard1.0/es/System.Net.Primitives.xml", - "ref/netstandard1.0/fr/System.Net.Primitives.xml", - "ref/netstandard1.0/it/System.Net.Primitives.xml", - "ref/netstandard1.0/ja/System.Net.Primitives.xml", - "ref/netstandard1.0/ko/System.Net.Primitives.xml", - "ref/netstandard1.0/ru/System.Net.Primitives.xml", - "ref/netstandard1.0/zh-hans/System.Net.Primitives.xml", - "ref/netstandard1.0/zh-hant/System.Net.Primitives.xml", - "ref/netstandard1.1/System.Net.Primitives.dll", - "ref/netstandard1.1/System.Net.Primitives.xml", - "ref/netstandard1.1/de/System.Net.Primitives.xml", - "ref/netstandard1.1/es/System.Net.Primitives.xml", - "ref/netstandard1.1/fr/System.Net.Primitives.xml", - "ref/netstandard1.1/it/System.Net.Primitives.xml", - "ref/netstandard1.1/ja/System.Net.Primitives.xml", - "ref/netstandard1.1/ko/System.Net.Primitives.xml", - "ref/netstandard1.1/ru/System.Net.Primitives.xml", - "ref/netstandard1.1/zh-hans/System.Net.Primitives.xml", - "ref/netstandard1.1/zh-hant/System.Net.Primitives.xml", - "ref/netstandard1.3/System.Net.Primitives.dll", - "ref/netstandard1.3/System.Net.Primitives.xml", - "ref/netstandard1.3/de/System.Net.Primitives.xml", - "ref/netstandard1.3/es/System.Net.Primitives.xml", - "ref/netstandard1.3/fr/System.Net.Primitives.xml", - "ref/netstandard1.3/it/System.Net.Primitives.xml", - "ref/netstandard1.3/ja/System.Net.Primitives.xml", - "ref/netstandard1.3/ko/System.Net.Primitives.xml", - "ref/netstandard1.3/ru/System.Net.Primitives.xml", - "ref/netstandard1.3/zh-hans/System.Net.Primitives.xml", - "ref/netstandard1.3/zh-hant/System.Net.Primitives.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.net.primitives.4.3.0.nupkg.sha512", - "system.net.primitives.nuspec" - ] - }, - "System.Net.Security/4.3.2": { - "sha512": "xT2jbYpbBo3ha87rViHoTA6WdvqOAW37drmqyx/6LD8p7HEPT2qgdxoimRzWtPg8Jh4X5G9BV2seeTv4x6FYlA==", - "type": "package", - "path": "system.net.security/4.3.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Net.Security.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Net.Security.dll", - "ref/netstandard1.3/System.Net.Security.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.6/System.Net.Security.dll", - "runtimes/win/lib/net46/System.Net.Security.dll", - "runtimes/win/lib/netstandard1.3/System.Net.Security.dll", - "runtimes/win7/lib/netcore50/_._", - "system.net.security.4.3.2.nupkg.sha512", - "system.net.security.nuspec" - ] - }, - "System.Net.Sockets/4.3.0": { - "sha512": "4y7ZUY6WMOme3PGWPD0OcEfqglKFPJJg61QDpCBhcK4o/SfrI5852k0tt2b4MLPr3J5NouaXviAkHZKAgiAJVQ==", - "type": "package", - "path": "system.net.sockets/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Net.Sockets.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Net.Sockets.dll", - "ref/netstandard1.3/System.Net.Sockets.dll", - "ref/netstandard1.3/System.Net.Sockets.xml", - "ref/netstandard1.3/de/System.Net.Sockets.xml", - "ref/netstandard1.3/es/System.Net.Sockets.xml", - "ref/netstandard1.3/fr/System.Net.Sockets.xml", - "ref/netstandard1.3/it/System.Net.Sockets.xml", - "ref/netstandard1.3/ja/System.Net.Sockets.xml", - "ref/netstandard1.3/ko/System.Net.Sockets.xml", - "ref/netstandard1.3/ru/System.Net.Sockets.xml", - "ref/netstandard1.3/zh-hans/System.Net.Sockets.xml", - "ref/netstandard1.3/zh-hant/System.Net.Sockets.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.net.sockets.4.3.0.nupkg.sha512", - "system.net.sockets.nuspec" - ] - }, - "System.Net.WebSockets.WebSocketProtocol/4.5.0": { - "sha512": "BLCyXRerXfuPgvN5Uoj1S+RHOuWnAU+wQ1kS/A4xmnd71Tsdrk7LG3YGHEi21/MNV2qtuxTsq93KuFSLQWiKFg==", - "type": "package", - "path": "system.net.websockets.websocketprotocol/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netcoreapp2.1/System.Net.WebSockets.WebSocketProtocol.dll", - "lib/netstandard2.0/System.Net.WebSockets.WebSocketProtocol.dll", - "ref/netstandard2.0/System.Net.WebSockets.WebSocketProtocol.dll", - "system.net.websockets.websocketprotocol.4.5.0.nupkg.sha512", - "system.net.websockets.websocketprotocol.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Numerics.Vectors/4.5.0": { - "sha512": "nATsBTD2CKr4AYN6eRszhX4sptImWmBJwB/U6XKCWWfnCcrTBw8XSCm3QA9gjppkHTr8OkXUY21MR91D3QZXsw==", - "type": "package", - "path": "system.numerics.vectors/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Numerics.Vectors.dll", - "lib/net46/System.Numerics.Vectors.xml", - "lib/netcoreapp2.0/_._", - "lib/netstandard1.0/System.Numerics.Vectors.dll", - "lib/netstandard1.0/System.Numerics.Vectors.xml", - "lib/netstandard2.0/System.Numerics.Vectors.dll", - "lib/netstandard2.0/System.Numerics.Vectors.xml", - "lib/portable-net45+win8+wp8+wpa81/System.Numerics.Vectors.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Numerics.Vectors.xml", - "lib/uap10.0.16299/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/System.Numerics.Vectors.dll", - "ref/net45/System.Numerics.Vectors.xml", - "ref/net46/System.Numerics.Vectors.dll", - "ref/net46/System.Numerics.Vectors.xml", - "ref/netcoreapp2.0/_._", - "ref/netstandard1.0/System.Numerics.Vectors.dll", - "ref/netstandard1.0/System.Numerics.Vectors.xml", - "ref/netstandard2.0/System.Numerics.Vectors.dll", - "ref/netstandard2.0/System.Numerics.Vectors.xml", - "ref/uap10.0.16299/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.numerics.vectors.4.5.0.nupkg.sha512", - "system.numerics.vectors.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.ObjectModel/4.3.0": { - "sha512": "QJvKPSE5vR0APHEUALotteV2u1TVk6pUHsNXbnsgKbYBWascWyxOc4kmexuV682MLwZNxuH1Pmk6rLFzfwZhIw==", - "type": "package", - "path": "system.objectmodel/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.ObjectModel.dll", - "lib/netstandard1.3/System.ObjectModel.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.ObjectModel.dll", - "ref/netcore50/System.ObjectModel.xml", - "ref/netcore50/de/System.ObjectModel.xml", - "ref/netcore50/es/System.ObjectModel.xml", - "ref/netcore50/fr/System.ObjectModel.xml", - "ref/netcore50/it/System.ObjectModel.xml", - "ref/netcore50/ja/System.ObjectModel.xml", - "ref/netcore50/ko/System.ObjectModel.xml", - "ref/netcore50/ru/System.ObjectModel.xml", - "ref/netcore50/zh-hans/System.ObjectModel.xml", - "ref/netcore50/zh-hant/System.ObjectModel.xml", - "ref/netstandard1.0/System.ObjectModel.dll", - "ref/netstandard1.0/System.ObjectModel.xml", - "ref/netstandard1.0/de/System.ObjectModel.xml", - "ref/netstandard1.0/es/System.ObjectModel.xml", - "ref/netstandard1.0/fr/System.ObjectModel.xml", - "ref/netstandard1.0/it/System.ObjectModel.xml", - "ref/netstandard1.0/ja/System.ObjectModel.xml", - "ref/netstandard1.0/ko/System.ObjectModel.xml", - "ref/netstandard1.0/ru/System.ObjectModel.xml", - "ref/netstandard1.0/zh-hans/System.ObjectModel.xml", - "ref/netstandard1.0/zh-hant/System.ObjectModel.xml", - "ref/netstandard1.3/System.ObjectModel.dll", - "ref/netstandard1.3/System.ObjectModel.xml", - "ref/netstandard1.3/de/System.ObjectModel.xml", - "ref/netstandard1.3/es/System.ObjectModel.xml", - "ref/netstandard1.3/fr/System.ObjectModel.xml", - "ref/netstandard1.3/it/System.ObjectModel.xml", - "ref/netstandard1.3/ja/System.ObjectModel.xml", - "ref/netstandard1.3/ko/System.ObjectModel.xml", - "ref/netstandard1.3/ru/System.ObjectModel.xml", - "ref/netstandard1.3/zh-hans/System.ObjectModel.xml", - "ref/netstandard1.3/zh-hant/System.ObjectModel.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.objectmodel.4.3.0.nupkg.sha512", - "system.objectmodel.nuspec" - ] - }, - "System.Private.DataContractSerialization/4.3.0": { - "sha512": "wEjvYddMMRV04qSvr/3+v9YsjnYKjxR6riG749gI5DdqXKvJtAKJSfXDxefu7TxlKvGiufQd6mVsI/NMmBIg8A==", - "type": "package", - "path": "system.private.datacontractserialization/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.3/System.Private.DataContractSerialization.dll", - "ref/netstandard/_._", - "runtimes/aot/lib/netcore50/System.Private.DataContractSerialization.dll", - "system.private.datacontractserialization.4.3.0.nupkg.sha512", - "system.private.datacontractserialization.nuspec" - ] - }, - "System.Private.ServiceModel/4.5.3": { - "sha512": "ancrQgJagx+yC4SZbuE+eShiEAUIF0E1d21TRSoy1C/rTwafAVcBr/fKibkq5TQzyy9uNil2tx2/iaUxsy0S9g==", - "type": "package", - "path": "system.private.servicemodel/4.5.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "ref/netstandard/_._", - "runtimes/unix/lib/netstandard1.3/System.Private.ServiceModel.dll", - "runtimes/unix/lib/netstandard2.0/System.Private.ServiceModel.dll", - "runtimes/win/lib/netcore50/System.Private.ServiceModel.dll", - "runtimes/win/lib/netstandard1.3/System.Private.ServiceModel.dll", - "runtimes/win/lib/netstandard2.0/System.Private.ServiceModel.dll", - "runtimes/win/lib/uap10.0.16300/System.Private.ServiceModel.dll", - "system.private.servicemodel.4.5.3.nupkg.sha512", - "system.private.servicemodel.nuspec", - "version.txt" - ] - }, - "System.Reactive/4.1.6": { - "sha512": "vCBMgMa1J4ZKvyJdnS6rl3BWiH5UZ6qLu2NyjxzBxIh1+IyQ/aPUs8A7HbZBCaQnoc7Y95+dzV3+PI+WxeQbpg==", - "type": "package", - "path": "system.reactive/4.1.6", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "lib/net46/System.Reactive.dll", - "lib/net46/System.Reactive.xml", - "lib/netstandard2.0/System.Reactive.dll", - "lib/netstandard2.0/System.Reactive.xml", - "lib/uap10.0.16299/System.Reactive.dll", - "lib/uap10.0.16299/System.Reactive.pri", - "lib/uap10.0.16299/System.Reactive.xml", - "lib/uap10.0/System.Reactive.dll", - "lib/uap10.0/System.Reactive.pri", - "lib/uap10.0/System.Reactive.xml", - "system.reactive.4.1.6.nupkg.sha512", - "system.reactive.nuspec" - ] - }, - "System.Reflection/4.3.0": { - "sha512": "IyW2ftYNzgMCgHBk8lQiy+G3+ydbU5tE+6PEqM5JJvIdeFKaXDSzHAPYDREPe6zpr5WJ1Fcma+rAFCIAV6+DMw==", - "type": "package", - "path": "system.reflection/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Reflection.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Reflection.dll", - "ref/netcore50/System.Reflection.dll", - "ref/netcore50/System.Reflection.xml", - "ref/netcore50/de/System.Reflection.xml", - "ref/netcore50/es/System.Reflection.xml", - "ref/netcore50/fr/System.Reflection.xml", - "ref/netcore50/it/System.Reflection.xml", - "ref/netcore50/ja/System.Reflection.xml", - "ref/netcore50/ko/System.Reflection.xml", - "ref/netcore50/ru/System.Reflection.xml", - "ref/netcore50/zh-hans/System.Reflection.xml", - "ref/netcore50/zh-hant/System.Reflection.xml", - "ref/netstandard1.0/System.Reflection.dll", - "ref/netstandard1.0/System.Reflection.xml", - "ref/netstandard1.0/de/System.Reflection.xml", - "ref/netstandard1.0/es/System.Reflection.xml", - "ref/netstandard1.0/fr/System.Reflection.xml", - "ref/netstandard1.0/it/System.Reflection.xml", - "ref/netstandard1.0/ja/System.Reflection.xml", - "ref/netstandard1.0/ko/System.Reflection.xml", - "ref/netstandard1.0/ru/System.Reflection.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.xml", - "ref/netstandard1.3/System.Reflection.dll", - "ref/netstandard1.3/System.Reflection.xml", - "ref/netstandard1.3/de/System.Reflection.xml", - "ref/netstandard1.3/es/System.Reflection.xml", - "ref/netstandard1.3/fr/System.Reflection.xml", - "ref/netstandard1.3/it/System.Reflection.xml", - "ref/netstandard1.3/ja/System.Reflection.xml", - "ref/netstandard1.3/ko/System.Reflection.xml", - "ref/netstandard1.3/ru/System.Reflection.xml", - "ref/netstandard1.3/zh-hans/System.Reflection.xml", - "ref/netstandard1.3/zh-hant/System.Reflection.xml", - "ref/netstandard1.5/System.Reflection.dll", - "ref/netstandard1.5/System.Reflection.xml", - "ref/netstandard1.5/de/System.Reflection.xml", - "ref/netstandard1.5/es/System.Reflection.xml", - "ref/netstandard1.5/fr/System.Reflection.xml", - "ref/netstandard1.5/it/System.Reflection.xml", - "ref/netstandard1.5/ja/System.Reflection.xml", - "ref/netstandard1.5/ko/System.Reflection.xml", - "ref/netstandard1.5/ru/System.Reflection.xml", - "ref/netstandard1.5/zh-hans/System.Reflection.xml", - "ref/netstandard1.5/zh-hant/System.Reflection.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.reflection.4.3.0.nupkg.sha512", - "system.reflection.nuspec" - ] - }, - "System.Reflection.DispatchProxy/4.5.0": { - "sha512": "+UW1hq11TNSeb+16rIk8hRQ02o339NFyzMc4ma/FqmxBzM30l1c2IherBB4ld1MNcenS48fz8tbt50OW4rVULA==", - "type": "package", - "path": "system.reflection.dispatchproxy/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net461/System.Reflection.DispatchProxy.dll", - "lib/netcoreapp2.0/System.Reflection.DispatchProxy.dll", - "lib/netstandard1.3/System.Reflection.DispatchProxy.dll", - "lib/netstandard2.0/System.Reflection.DispatchProxy.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/netstandard1.3/System.Reflection.DispatchProxy.dll", - "ref/netstandard1.3/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/de/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/es/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/fr/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/it/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/ja/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/ko/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/ru/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/zh-hans/System.Reflection.DispatchProxy.xml", - "ref/netstandard1.3/zh-hant/System.Reflection.DispatchProxy.xml", - "ref/netstandard2.0/System.Reflection.DispatchProxy.dll", - "ref/netstandard2.0/System.Reflection.DispatchProxy.xml", - "ref/uap10.0.16299/System.Reflection.DispatchProxy.dll", - "ref/uap10.0.16299/System.Reflection.DispatchProxy.xml", - "ref/uap10.0.16300/System.Reflection.DispatchProxy.dll", - "ref/uap10.0.16300/System.Reflection.DispatchProxy.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Reflection.DispatchProxy.dll", - "runtimes/win-aot/lib/uap10.0.16299/System.Reflection.DispatchProxy.dll", - "runtimes/win/lib/uap10.0.16299/System.Reflection.DispatchProxy.dll", - "system.reflection.dispatchproxy.4.5.0.nupkg.sha512", - "system.reflection.dispatchproxy.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Reflection.Emit/4.3.0": { - "sha512": "vkUFFGejarllQQ8RKkdfuBUQpVlTR9HMDEawKOBDajOSGN08Bz8EjC0zi2fcE7RXQikLbEb1WYJQP3So8mmIGA==", - "type": "package", - "path": "system.reflection.emit/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/monotouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Reflection.Emit.dll", - "lib/netstandard1.3/System.Reflection.Emit.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/net45/_._", - "ref/netstandard1.1/System.Reflection.Emit.dll", - "ref/netstandard1.1/System.Reflection.Emit.xml", - "ref/netstandard1.1/de/System.Reflection.Emit.xml", - "ref/netstandard1.1/es/System.Reflection.Emit.xml", - "ref/netstandard1.1/fr/System.Reflection.Emit.xml", - "ref/netstandard1.1/it/System.Reflection.Emit.xml", - "ref/netstandard1.1/ja/System.Reflection.Emit.xml", - "ref/netstandard1.1/ko/System.Reflection.Emit.xml", - "ref/netstandard1.1/ru/System.Reflection.Emit.xml", - "ref/netstandard1.1/zh-hans/System.Reflection.Emit.xml", - "ref/netstandard1.1/zh-hant/System.Reflection.Emit.xml", - "ref/xamarinmac20/_._", - "system.reflection.emit.4.3.0.nupkg.sha512", - "system.reflection.emit.nuspec" - ] - }, - "System.Reflection.Emit.ILGeneration/4.3.0": { - "sha512": "6b5fYr9ksZR6SYVzNzBqXQmAaGtY1mWYnpQAarBKp+C79NhUPRtX1bs4B5BS8nXzObcwVKc1fk+jVyCKCshdaQ==", - "type": "package", - "path": "system.reflection.emit.ilgeneration/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Reflection.Emit.ILGeneration.dll", - "lib/netstandard1.3/System.Reflection.Emit.ILGeneration.dll", - "lib/portable-net45+wp8/_._", - "lib/wp80/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netstandard1.0/System.Reflection.Emit.ILGeneration.dll", - "ref/netstandard1.0/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/de/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/es/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/fr/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/it/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/ja/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/ko/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/ru/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Emit.ILGeneration.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Emit.ILGeneration.xml", - "ref/portable-net45+wp8/_._", - "ref/wp80/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/_._", - "system.reflection.emit.ilgeneration.4.3.0.nupkg.sha512", - "system.reflection.emit.ilgeneration.nuspec" - ] - }, - "System.Reflection.Emit.Lightweight/4.3.0": { - "sha512": "rVivBylr0ISQegifkgJvo4mLdk651qB8lBS1UKg6xgRW8yo0Enwpu5OpYz+we6n9go97QaMdzl/wGafPGrKUNQ==", - "type": "package", - "path": "system.reflection.emit.lightweight/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Reflection.Emit.Lightweight.dll", - "lib/netstandard1.3/System.Reflection.Emit.Lightweight.dll", - "lib/portable-net45+wp8/_._", - "lib/wp80/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netstandard1.0/System.Reflection.Emit.Lightweight.dll", - "ref/netstandard1.0/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/de/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/es/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/fr/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/it/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/ja/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/ko/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/ru/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Emit.Lightweight.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Emit.Lightweight.xml", - "ref/portable-net45+wp8/_._", - "ref/wp80/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/_._", - "system.reflection.emit.lightweight.4.3.0.nupkg.sha512", - "system.reflection.emit.lightweight.nuspec" - ] - }, - "System.Reflection.Extensions/4.3.0": { - "sha512": "Bs/ZksjX/Zq2QyqwK+mBoBtlWChabiangloGTU78zgjZ5zRPA/oZsDOiRZ1CsLgOjBQAzjm0ehdShpq4glsEdQ==", - "type": "package", - "path": "system.reflection.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Reflection.Extensions.dll", - "ref/netcore50/System.Reflection.Extensions.xml", - "ref/netcore50/de/System.Reflection.Extensions.xml", - "ref/netcore50/es/System.Reflection.Extensions.xml", - "ref/netcore50/fr/System.Reflection.Extensions.xml", - "ref/netcore50/it/System.Reflection.Extensions.xml", - "ref/netcore50/ja/System.Reflection.Extensions.xml", - "ref/netcore50/ko/System.Reflection.Extensions.xml", - "ref/netcore50/ru/System.Reflection.Extensions.xml", - "ref/netcore50/zh-hans/System.Reflection.Extensions.xml", - "ref/netcore50/zh-hant/System.Reflection.Extensions.xml", - "ref/netstandard1.0/System.Reflection.Extensions.dll", - "ref/netstandard1.0/System.Reflection.Extensions.xml", - "ref/netstandard1.0/de/System.Reflection.Extensions.xml", - "ref/netstandard1.0/es/System.Reflection.Extensions.xml", - "ref/netstandard1.0/fr/System.Reflection.Extensions.xml", - "ref/netstandard1.0/it/System.Reflection.Extensions.xml", - "ref/netstandard1.0/ja/System.Reflection.Extensions.xml", - "ref/netstandard1.0/ko/System.Reflection.Extensions.xml", - "ref/netstandard1.0/ru/System.Reflection.Extensions.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Extensions.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Extensions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.reflection.extensions.4.3.0.nupkg.sha512", - "system.reflection.extensions.nuspec" - ] - }, - "System.Reflection.Metadata/1.6.0": { - "sha512": "9SJ2Zu3Gux2ni4qOhqaOm9ZHyqLsahWAwUpKXh/lz9472vDYwj3CEMQFpV+Dzrat0amtqxSdwGWzjP3cmwG6IA==", - "type": "package", - "path": "system.reflection.metadata/1.6.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.1/System.Reflection.Metadata.dll", - "lib/netstandard1.1/System.Reflection.Metadata.xml", - "lib/netstandard2.0/System.Reflection.Metadata.dll", - "lib/netstandard2.0/System.Reflection.Metadata.xml", - "lib/portable-net45+win8/System.Reflection.Metadata.dll", - "lib/portable-net45+win8/System.Reflection.Metadata.xml", - "system.reflection.metadata.1.6.0.nupkg.sha512", - "system.reflection.metadata.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Reflection.Primitives/4.3.0": { - "sha512": "1LnMkF9aXKuQAgYzjoiQaL9mwY7oY6KdaO/zzeLMynNBEqKoUfLi5TiKIewoAF+hkxfGTZsjkjsF1jRL4uSeqg==", - "type": "package", - "path": "system.reflection.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Reflection.Primitives.dll", - "ref/netcore50/System.Reflection.Primitives.xml", - "ref/netcore50/de/System.Reflection.Primitives.xml", - "ref/netcore50/es/System.Reflection.Primitives.xml", - "ref/netcore50/fr/System.Reflection.Primitives.xml", - "ref/netcore50/it/System.Reflection.Primitives.xml", - "ref/netcore50/ja/System.Reflection.Primitives.xml", - "ref/netcore50/ko/System.Reflection.Primitives.xml", - "ref/netcore50/ru/System.Reflection.Primitives.xml", - "ref/netcore50/zh-hans/System.Reflection.Primitives.xml", - "ref/netcore50/zh-hant/System.Reflection.Primitives.xml", - "ref/netstandard1.0/System.Reflection.Primitives.dll", - "ref/netstandard1.0/System.Reflection.Primitives.xml", - "ref/netstandard1.0/de/System.Reflection.Primitives.xml", - "ref/netstandard1.0/es/System.Reflection.Primitives.xml", - "ref/netstandard1.0/fr/System.Reflection.Primitives.xml", - "ref/netstandard1.0/it/System.Reflection.Primitives.xml", - "ref/netstandard1.0/ja/System.Reflection.Primitives.xml", - "ref/netstandard1.0/ko/System.Reflection.Primitives.xml", - "ref/netstandard1.0/ru/System.Reflection.Primitives.xml", - "ref/netstandard1.0/zh-hans/System.Reflection.Primitives.xml", - "ref/netstandard1.0/zh-hant/System.Reflection.Primitives.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.reflection.primitives.4.3.0.nupkg.sha512", - "system.reflection.primitives.nuspec" - ] - }, - "System.Reflection.TypeExtensions/4.5.1": { - "sha512": "fO8GMEkgoKioJ7cglZbhcnBgkCWWn9poS3G2jevS+fuwW9xJXMx7/1kr7dkwOJfo0pWqxLFWVcxlOr+WeK5ipA==", - "type": "package", - "path": "system.reflection.typeextensions/4.5.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Reflection.TypeExtensions.dll", - "lib/net461/System.Reflection.TypeExtensions.dll", - "lib/netcore50/System.Reflection.TypeExtensions.dll", - "lib/netcoreapp1.0/System.Reflection.TypeExtensions.dll", - "lib/netcoreapp2.0/_._", - "lib/netstandard1.3/System.Reflection.TypeExtensions.dll", - "lib/netstandard1.5/System.Reflection.TypeExtensions.dll", - "lib/netstandard2.0/System.Reflection.TypeExtensions.dll", - "lib/uap10.0.16299/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Reflection.TypeExtensions.dll", - "ref/net461/System.Reflection.TypeExtensions.dll", - "ref/net461/System.Reflection.TypeExtensions.xml", - "ref/netcoreapp2.0/_._", - "ref/netstandard1.3/System.Reflection.TypeExtensions.dll", - "ref/netstandard1.3/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/de/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/es/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/fr/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/it/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/ja/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/ko/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/ru/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/zh-hans/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.3/zh-hant/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/System.Reflection.TypeExtensions.dll", - "ref/netstandard1.5/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/de/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/es/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/fr/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/it/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/ja/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/ko/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/ru/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/zh-hans/System.Reflection.TypeExtensions.xml", - "ref/netstandard1.5/zh-hant/System.Reflection.TypeExtensions.xml", - "ref/netstandard2.0/System.Reflection.TypeExtensions.dll", - "ref/netstandard2.0/System.Reflection.TypeExtensions.xml", - "ref/uap10.0.16299/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Reflection.TypeExtensions.dll", - "runtimes/aot/lib/uap10.0.16299/_._", - "system.reflection.typeextensions.4.5.1.nupkg.sha512", - "system.reflection.typeextensions.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Resources.ResourceManager/4.3.0": { - "sha512": "kGfbKPHEjQj8Uq1Apgj4jBStkRJkZ0Hdr0Jv3+aL7WGrAZVLF5Rh5h0Yc3FgDB5uXDbHiJk/WhBaZPVwKmuB1A==", - "type": "package", - "path": "system.resources.resourcemanager/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Resources.ResourceManager.dll", - "ref/netcore50/System.Resources.ResourceManager.xml", - "ref/netcore50/de/System.Resources.ResourceManager.xml", - "ref/netcore50/es/System.Resources.ResourceManager.xml", - "ref/netcore50/fr/System.Resources.ResourceManager.xml", - "ref/netcore50/it/System.Resources.ResourceManager.xml", - "ref/netcore50/ja/System.Resources.ResourceManager.xml", - "ref/netcore50/ko/System.Resources.ResourceManager.xml", - "ref/netcore50/ru/System.Resources.ResourceManager.xml", - "ref/netcore50/zh-hans/System.Resources.ResourceManager.xml", - "ref/netcore50/zh-hant/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/System.Resources.ResourceManager.dll", - "ref/netstandard1.0/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/de/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/es/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/fr/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/it/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/ja/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/ko/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/ru/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/zh-hans/System.Resources.ResourceManager.xml", - "ref/netstandard1.0/zh-hant/System.Resources.ResourceManager.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.resources.resourcemanager.4.3.0.nupkg.sha512", - "system.resources.resourcemanager.nuspec" - ] - }, - "System.Runtime/4.3.0": { - "sha512": "kqsiSfCAc8+v3Ez719s21lGthxuNi6lhAGmCGH3jdL9KMK+T8V9zsFrzQ/enDL1ISwTWRlcFh2Nq5yFx6wcU+w==", - "type": "package", - "path": "system.runtime/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Runtime.dll", - "lib/portable-net45+win8+wp80+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Runtime.dll", - "ref/netcore50/System.Runtime.dll", - "ref/netcore50/System.Runtime.xml", - "ref/netcore50/de/System.Runtime.xml", - "ref/netcore50/es/System.Runtime.xml", - "ref/netcore50/fr/System.Runtime.xml", - "ref/netcore50/it/System.Runtime.xml", - "ref/netcore50/ja/System.Runtime.xml", - "ref/netcore50/ko/System.Runtime.xml", - "ref/netcore50/ru/System.Runtime.xml", - "ref/netcore50/zh-hans/System.Runtime.xml", - "ref/netcore50/zh-hant/System.Runtime.xml", - "ref/netstandard1.0/System.Runtime.dll", - "ref/netstandard1.0/System.Runtime.xml", - "ref/netstandard1.0/de/System.Runtime.xml", - "ref/netstandard1.0/es/System.Runtime.xml", - "ref/netstandard1.0/fr/System.Runtime.xml", - "ref/netstandard1.0/it/System.Runtime.xml", - "ref/netstandard1.0/ja/System.Runtime.xml", - "ref/netstandard1.0/ko/System.Runtime.xml", - "ref/netstandard1.0/ru/System.Runtime.xml", - "ref/netstandard1.0/zh-hans/System.Runtime.xml", - "ref/netstandard1.0/zh-hant/System.Runtime.xml", - "ref/netstandard1.2/System.Runtime.dll", - "ref/netstandard1.2/System.Runtime.xml", - "ref/netstandard1.2/de/System.Runtime.xml", - "ref/netstandard1.2/es/System.Runtime.xml", - "ref/netstandard1.2/fr/System.Runtime.xml", - "ref/netstandard1.2/it/System.Runtime.xml", - "ref/netstandard1.2/ja/System.Runtime.xml", - "ref/netstandard1.2/ko/System.Runtime.xml", - "ref/netstandard1.2/ru/System.Runtime.xml", - "ref/netstandard1.2/zh-hans/System.Runtime.xml", - "ref/netstandard1.2/zh-hant/System.Runtime.xml", - "ref/netstandard1.3/System.Runtime.dll", - "ref/netstandard1.3/System.Runtime.xml", - "ref/netstandard1.3/de/System.Runtime.xml", - "ref/netstandard1.3/es/System.Runtime.xml", - "ref/netstandard1.3/fr/System.Runtime.xml", - "ref/netstandard1.3/it/System.Runtime.xml", - "ref/netstandard1.3/ja/System.Runtime.xml", - "ref/netstandard1.3/ko/System.Runtime.xml", - "ref/netstandard1.3/ru/System.Runtime.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.xml", - "ref/netstandard1.5/System.Runtime.dll", - "ref/netstandard1.5/System.Runtime.xml", - "ref/netstandard1.5/de/System.Runtime.xml", - "ref/netstandard1.5/es/System.Runtime.xml", - "ref/netstandard1.5/fr/System.Runtime.xml", - "ref/netstandard1.5/it/System.Runtime.xml", - "ref/netstandard1.5/ja/System.Runtime.xml", - "ref/netstandard1.5/ko/System.Runtime.xml", - "ref/netstandard1.5/ru/System.Runtime.xml", - "ref/netstandard1.5/zh-hans/System.Runtime.xml", - "ref/netstandard1.5/zh-hant/System.Runtime.xml", - "ref/portable-net45+win8+wp80+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.4.3.0.nupkg.sha512", - "system.runtime.nuspec" - ] - }, - "System.Runtime.CompilerServices.Unsafe/4.5.2": { - "sha512": "wprSFgext8cwqymChhrBLu62LMg/1u92bU+VOwyfBimSPVFXtsNqEWC92Pf9ofzJFlk4IHmJA75EDJn1b2goAQ==", - "type": "package", - "path": "system.runtime.compilerservices.unsafe/4.5.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.dll", - "lib/netcoreapp2.0/System.Runtime.CompilerServices.Unsafe.xml", - "lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll", - "lib/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml", - "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll", - "lib/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml", - "ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.dll", - "ref/netstandard1.0/System.Runtime.CompilerServices.Unsafe.xml", - "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.dll", - "ref/netstandard2.0/System.Runtime.CompilerServices.Unsafe.xml", - "system.runtime.compilerservices.unsafe.4.5.2.nupkg.sha512", - "system.runtime.compilerservices.unsafe.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Runtime.Extensions/4.3.0": { - "sha512": "aAoysZwr1QJvhoeqU4KupPQytPAy+L3imfrLYYxW1XNpre9/fMjmCtgq48EuXdUHckkTY7+ARMd4d4YopmBbvA==", - "type": "package", - "path": "system.runtime.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Runtime.Extensions.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Runtime.Extensions.dll", - "ref/netcore50/System.Runtime.Extensions.dll", - "ref/netcore50/System.Runtime.Extensions.xml", - "ref/netcore50/de/System.Runtime.Extensions.xml", - "ref/netcore50/es/System.Runtime.Extensions.xml", - "ref/netcore50/fr/System.Runtime.Extensions.xml", - "ref/netcore50/it/System.Runtime.Extensions.xml", - "ref/netcore50/ja/System.Runtime.Extensions.xml", - "ref/netcore50/ko/System.Runtime.Extensions.xml", - "ref/netcore50/ru/System.Runtime.Extensions.xml", - "ref/netcore50/zh-hans/System.Runtime.Extensions.xml", - "ref/netcore50/zh-hant/System.Runtime.Extensions.xml", - "ref/netstandard1.0/System.Runtime.Extensions.dll", - "ref/netstandard1.0/System.Runtime.Extensions.xml", - "ref/netstandard1.0/de/System.Runtime.Extensions.xml", - "ref/netstandard1.0/es/System.Runtime.Extensions.xml", - "ref/netstandard1.0/fr/System.Runtime.Extensions.xml", - "ref/netstandard1.0/it/System.Runtime.Extensions.xml", - "ref/netstandard1.0/ja/System.Runtime.Extensions.xml", - "ref/netstandard1.0/ko/System.Runtime.Extensions.xml", - "ref/netstandard1.0/ru/System.Runtime.Extensions.xml", - "ref/netstandard1.0/zh-hans/System.Runtime.Extensions.xml", - "ref/netstandard1.0/zh-hant/System.Runtime.Extensions.xml", - "ref/netstandard1.3/System.Runtime.Extensions.dll", - "ref/netstandard1.3/System.Runtime.Extensions.xml", - "ref/netstandard1.3/de/System.Runtime.Extensions.xml", - "ref/netstandard1.3/es/System.Runtime.Extensions.xml", - "ref/netstandard1.3/fr/System.Runtime.Extensions.xml", - "ref/netstandard1.3/it/System.Runtime.Extensions.xml", - "ref/netstandard1.3/ja/System.Runtime.Extensions.xml", - "ref/netstandard1.3/ko/System.Runtime.Extensions.xml", - "ref/netstandard1.3/ru/System.Runtime.Extensions.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.Extensions.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.Extensions.xml", - "ref/netstandard1.5/System.Runtime.Extensions.dll", - "ref/netstandard1.5/System.Runtime.Extensions.xml", - "ref/netstandard1.5/de/System.Runtime.Extensions.xml", - "ref/netstandard1.5/es/System.Runtime.Extensions.xml", - "ref/netstandard1.5/fr/System.Runtime.Extensions.xml", - "ref/netstandard1.5/it/System.Runtime.Extensions.xml", - "ref/netstandard1.5/ja/System.Runtime.Extensions.xml", - "ref/netstandard1.5/ko/System.Runtime.Extensions.xml", - "ref/netstandard1.5/ru/System.Runtime.Extensions.xml", - "ref/netstandard1.5/zh-hans/System.Runtime.Extensions.xml", - "ref/netstandard1.5/zh-hant/System.Runtime.Extensions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.extensions.4.3.0.nupkg.sha512", - "system.runtime.extensions.nuspec" - ] - }, - "System.Runtime.Handles/4.3.0": { - "sha512": "CluvHdVUv54BvLTOCCyybugreDNk/rR8unMPruzXDtxSjvrQOU3M4R831/lQf4YI8VYp668FGQa/01E+Rq8PEQ==", - "type": "package", - "path": "system.runtime.handles/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/_._", - "ref/netstandard1.3/System.Runtime.Handles.dll", - "ref/netstandard1.3/System.Runtime.Handles.xml", - "ref/netstandard1.3/de/System.Runtime.Handles.xml", - "ref/netstandard1.3/es/System.Runtime.Handles.xml", - "ref/netstandard1.3/fr/System.Runtime.Handles.xml", - "ref/netstandard1.3/it/System.Runtime.Handles.xml", - "ref/netstandard1.3/ja/System.Runtime.Handles.xml", - "ref/netstandard1.3/ko/System.Runtime.Handles.xml", - "ref/netstandard1.3/ru/System.Runtime.Handles.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.Handles.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.Handles.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.handles.4.3.0.nupkg.sha512", - "system.runtime.handles.nuspec" - ] - }, - "System.Runtime.InteropServices/4.3.0": { - "sha512": "ZQeZw+ZU77ua1nFXycYM5G8oioFZe+N84qC/XUg1BEBl7z9luZcyjLu7+4H0yJuNfn1hOAiAAZ3u5us/lj9w2Q==", - "type": "package", - "path": "system.runtime.interopservices/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net462/System.Runtime.InteropServices.dll", - "lib/net463/System.Runtime.InteropServices.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net462/System.Runtime.InteropServices.dll", - "ref/net463/System.Runtime.InteropServices.dll", - "ref/netcore50/System.Runtime.InteropServices.dll", - "ref/netcore50/System.Runtime.InteropServices.xml", - "ref/netcore50/de/System.Runtime.InteropServices.xml", - "ref/netcore50/es/System.Runtime.InteropServices.xml", - "ref/netcore50/fr/System.Runtime.InteropServices.xml", - "ref/netcore50/it/System.Runtime.InteropServices.xml", - "ref/netcore50/ja/System.Runtime.InteropServices.xml", - "ref/netcore50/ko/System.Runtime.InteropServices.xml", - "ref/netcore50/ru/System.Runtime.InteropServices.xml", - "ref/netcore50/zh-hans/System.Runtime.InteropServices.xml", - "ref/netcore50/zh-hant/System.Runtime.InteropServices.xml", - "ref/netcoreapp1.1/System.Runtime.InteropServices.dll", - "ref/netstandard1.1/System.Runtime.InteropServices.dll", - "ref/netstandard1.1/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.1/zh-hant/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/System.Runtime.InteropServices.dll", - "ref/netstandard1.2/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.2/zh-hant/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/System.Runtime.InteropServices.dll", - "ref/netstandard1.3/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/System.Runtime.InteropServices.dll", - "ref/netstandard1.5/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/de/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/es/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/fr/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/it/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/ja/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/ko/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/ru/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/zh-hans/System.Runtime.InteropServices.xml", - "ref/netstandard1.5/zh-hant/System.Runtime.InteropServices.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.interopservices.4.3.0.nupkg.sha512", - "system.runtime.interopservices.nuspec" - ] - }, - "System.Runtime.InteropServices.RuntimeInformation/4.3.0": { - "sha512": "b0kFMpo8yeYtJ0yIXyde4xxa9Xpsn9GlCA0DnLdI4Cd77z3IzkKGPKx4NlCE4AoDInm/PStyVKSfP7FWaimtGw==", - "type": "package", - "path": "system.runtime.interopservices.runtimeinformation/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/win8/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/wpa81/System.Runtime.InteropServices.RuntimeInformation.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/unix/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/win/lib/net45/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/win/lib/netcore50/System.Runtime.InteropServices.RuntimeInformation.dll", - "runtimes/win/lib/netstandard1.1/System.Runtime.InteropServices.RuntimeInformation.dll", - "system.runtime.interopservices.runtimeinformation.4.3.0.nupkg.sha512", - "system.runtime.interopservices.runtimeinformation.nuspec" - ] - }, - "System.Runtime.InteropServices.WindowsRuntime/4.3.0": { - "sha512": "J4GUi3xZQLUBasNwZnjrffN8i5wpHrBtZoLG+OhRyGo/+YunMRWWtwoMDlUAIdmX0uRfpHIBDSV6zyr3yf00TA==", - "type": "package", - "path": "system.runtime.interopservices.windowsruntime/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Runtime.InteropServices.WindowsRuntime.dll", - "lib/netstandard1.3/System.Runtime.InteropServices.WindowsRuntime.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios1/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Runtime.InteropServices.WindowsRuntime.dll", - "ref/netcore50/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/de/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/es/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/fr/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/it/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/ja/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/ko/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/ru/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/zh-hans/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netcore50/zh-hant/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/System.Runtime.InteropServices.WindowsRuntime.dll", - "ref/netstandard1.0/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/de/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/es/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/fr/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/it/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/ja/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/ko/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/ru/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/zh-hans/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/netstandard1.0/zh-hant/System.Runtime.InteropServices.WindowsRuntime.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Runtime.InteropServices.WindowsRuntime.dll", - "system.runtime.interopservices.windowsruntime.4.3.0.nupkg.sha512", - "system.runtime.interopservices.windowsruntime.nuspec" - ] - }, - "System.Runtime.Numerics/4.3.0": { - "sha512": "PjR/qo5+xITUgeU7HCGf4c40augniiFLRQjPDiM8Fie9nGxsfGVOjB9BQycYON3ZWT9joQQ1d62HxA45Kvf9NA==", - "type": "package", - "path": "system.runtime.numerics/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Runtime.Numerics.dll", - "lib/netstandard1.3/System.Runtime.Numerics.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Runtime.Numerics.dll", - "ref/netcore50/System.Runtime.Numerics.xml", - "ref/netcore50/de/System.Runtime.Numerics.xml", - "ref/netcore50/es/System.Runtime.Numerics.xml", - "ref/netcore50/fr/System.Runtime.Numerics.xml", - "ref/netcore50/it/System.Runtime.Numerics.xml", - "ref/netcore50/ja/System.Runtime.Numerics.xml", - "ref/netcore50/ko/System.Runtime.Numerics.xml", - "ref/netcore50/ru/System.Runtime.Numerics.xml", - "ref/netcore50/zh-hans/System.Runtime.Numerics.xml", - "ref/netcore50/zh-hant/System.Runtime.Numerics.xml", - "ref/netstandard1.1/System.Runtime.Numerics.dll", - "ref/netstandard1.1/System.Runtime.Numerics.xml", - "ref/netstandard1.1/de/System.Runtime.Numerics.xml", - "ref/netstandard1.1/es/System.Runtime.Numerics.xml", - "ref/netstandard1.1/fr/System.Runtime.Numerics.xml", - "ref/netstandard1.1/it/System.Runtime.Numerics.xml", - "ref/netstandard1.1/ja/System.Runtime.Numerics.xml", - "ref/netstandard1.1/ko/System.Runtime.Numerics.xml", - "ref/netstandard1.1/ru/System.Runtime.Numerics.xml", - "ref/netstandard1.1/zh-hans/System.Runtime.Numerics.xml", - "ref/netstandard1.1/zh-hant/System.Runtime.Numerics.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.numerics.4.3.0.nupkg.sha512", - "system.runtime.numerics.nuspec" - ] - }, - "System.Runtime.Serialization.Primitives/4.3.0": { - "sha512": "/Pc7qt9WdQKekfLm5KccMF7s42ce9p9JRA+cbaFg17M/CnOSPWUvKYf3ZoCT907YPecrGF3izm7Gs3xughIXrg==", - "type": "package", - "path": "system.runtime.serialization.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.Runtime.Serialization.Primitives.dll", - "lib/netcore50/System.Runtime.Serialization.Primitives.dll", - "lib/netstandard1.3/System.Runtime.Serialization.Primitives.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.Runtime.Serialization.Primitives.dll", - "ref/netcore50/System.Runtime.Serialization.Primitives.dll", - "ref/netcore50/System.Runtime.Serialization.Primitives.xml", - "ref/netcore50/de/System.Runtime.Serialization.Primitives.xml", - "ref/netcore50/es/System.Runtime.Serialization.Primitives.xml", - "ref/netcore50/fr/System.Runtime.Serialization.Primitives.xml", - "ref/netcore50/it/System.Runtime.Serialization.Primitives.xml", - "ref/netcore50/ja/System.Runtime.Serialization.Primitives.xml", - "ref/netcore50/ko/System.Runtime.Serialization.Primitives.xml", - "ref/netcore50/ru/System.Runtime.Serialization.Primitives.xml", - "ref/netcore50/zh-hans/System.Runtime.Serialization.Primitives.xml", - "ref/netcore50/zh-hant/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.0/System.Runtime.Serialization.Primitives.dll", - "ref/netstandard1.0/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.0/de/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.0/es/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.0/fr/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.0/it/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.0/ja/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.0/ko/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.0/ru/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.0/zh-hans/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.0/zh-hant/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.3/System.Runtime.Serialization.Primitives.dll", - "ref/netstandard1.3/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.3/de/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.3/es/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.3/fr/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.3/it/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.3/ja/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.3/ko/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.3/ru/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.Serialization.Primitives.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.Serialization.Primitives.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Runtime.Serialization.Primitives.dll", - "system.runtime.serialization.primitives.4.3.0.nupkg.sha512", - "system.runtime.serialization.primitives.nuspec" - ] - }, - "System.Runtime.Serialization.Xml/4.3.0": { - "sha512": "v5BPUVQNkxkHh80Lhe8YM3iVSN0zYP5Xm9Zw47aoSu6+SyRGbGbEC6i/Vq3gUufIjPSgi16fs1wBLEZNU9zXyQ==", - "type": "package", - "path": "system.runtime.serialization.xml/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.Runtime.Serialization.Xml.dll", - "lib/netcore50/System.Runtime.Serialization.Xml.dll", - "lib/netstandard1.3/System.Runtime.Serialization.Xml.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.Runtime.Serialization.Xml.dll", - "ref/netcore50/System.Runtime.Serialization.Xml.dll", - "ref/netcore50/System.Runtime.Serialization.Xml.xml", - "ref/netcore50/de/System.Runtime.Serialization.Xml.xml", - "ref/netcore50/es/System.Runtime.Serialization.Xml.xml", - "ref/netcore50/fr/System.Runtime.Serialization.Xml.xml", - "ref/netcore50/it/System.Runtime.Serialization.Xml.xml", - "ref/netcore50/ja/System.Runtime.Serialization.Xml.xml", - "ref/netcore50/ko/System.Runtime.Serialization.Xml.xml", - "ref/netcore50/ru/System.Runtime.Serialization.Xml.xml", - "ref/netcore50/zh-hans/System.Runtime.Serialization.Xml.xml", - "ref/netcore50/zh-hant/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.0/System.Runtime.Serialization.Xml.dll", - "ref/netstandard1.0/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.0/de/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.0/es/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.0/fr/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.0/it/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.0/ja/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.0/ko/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.0/ru/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.0/zh-hans/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.0/zh-hant/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.3/System.Runtime.Serialization.Xml.dll", - "ref/netstandard1.3/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.3/de/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.3/es/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.3/fr/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.3/it/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.3/ja/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.3/ko/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.3/ru/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.3/zh-hans/System.Runtime.Serialization.Xml.xml", - "ref/netstandard1.3/zh-hant/System.Runtime.Serialization.Xml.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.runtime.serialization.xml.4.3.0.nupkg.sha512", - "system.runtime.serialization.xml.nuspec" - ] - }, - "System.Security.AccessControl/4.5.0": { - "sha512": "6RQtcT+TyDgLUFsAnmmdfRRGdLYKxSZGkSPOd052tvYFxOTMaQb6ANlwhGqZtNO52PosaCoXu7uatFneujAxmA==", - "type": "package", - "path": "system.security.accesscontrol/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net46/System.Security.AccessControl.dll", - "lib/net461/System.Security.AccessControl.dll", - "lib/netstandard1.3/System.Security.AccessControl.dll", - "lib/netstandard2.0/System.Security.AccessControl.dll", - "lib/uap10.0.16299/_._", - "ref/net46/System.Security.AccessControl.dll", - "ref/net461/System.Security.AccessControl.dll", - "ref/net461/System.Security.AccessControl.xml", - "ref/netstandard1.3/System.Security.AccessControl.dll", - "ref/netstandard1.3/System.Security.AccessControl.xml", - "ref/netstandard1.3/de/System.Security.AccessControl.xml", - "ref/netstandard1.3/es/System.Security.AccessControl.xml", - "ref/netstandard1.3/fr/System.Security.AccessControl.xml", - "ref/netstandard1.3/it/System.Security.AccessControl.xml", - "ref/netstandard1.3/ja/System.Security.AccessControl.xml", - "ref/netstandard1.3/ko/System.Security.AccessControl.xml", - "ref/netstandard1.3/ru/System.Security.AccessControl.xml", - "ref/netstandard1.3/zh-hans/System.Security.AccessControl.xml", - "ref/netstandard1.3/zh-hant/System.Security.AccessControl.xml", - "ref/netstandard2.0/System.Security.AccessControl.dll", - "ref/netstandard2.0/System.Security.AccessControl.xml", - "ref/uap10.0.16299/_._", - "runtimes/win/lib/net46/System.Security.AccessControl.dll", - "runtimes/win/lib/net461/System.Security.AccessControl.dll", - "runtimes/win/lib/netcoreapp2.0/System.Security.AccessControl.dll", - "runtimes/win/lib/netstandard1.3/System.Security.AccessControl.dll", - "runtimes/win/lib/uap10.0.16299/_._", - "system.security.accesscontrol.4.5.0.nupkg.sha512", - "system.security.accesscontrol.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Security.Claims/4.3.0": { - "sha512": "q3K5CAH2wFGisxZFRI7r/KdGQrPPodUfgOIaDQ161E0zZt6hOTR+KFJ4G387roIN8Ww+sYiiyWJE3wU5Ttcshg==", - "type": "package", - "path": "system.security.claims/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Claims.dll", - "lib/netstandard1.3/System.Security.Claims.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Claims.dll", - "ref/netstandard1.3/System.Security.Claims.dll", - "ref/netstandard1.3/System.Security.Claims.xml", - "ref/netstandard1.3/de/System.Security.Claims.xml", - "ref/netstandard1.3/es/System.Security.Claims.xml", - "ref/netstandard1.3/fr/System.Security.Claims.xml", - "ref/netstandard1.3/it/System.Security.Claims.xml", - "ref/netstandard1.3/ja/System.Security.Claims.xml", - "ref/netstandard1.3/ko/System.Security.Claims.xml", - "ref/netstandard1.3/ru/System.Security.Claims.xml", - "ref/netstandard1.3/zh-hans/System.Security.Claims.xml", - "ref/netstandard1.3/zh-hant/System.Security.Claims.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.security.claims.4.3.0.nupkg.sha512", - "system.security.claims.nuspec" - ] - }, - "System.Security.Cryptography.Algorithms/4.3.1": { - "sha512": "DVUblnRfnarrI5olEC2B/OCsJQd0anjVaObQMndHSc43efbc88/RMOlDyg/EyY0ix5ecyZMXS8zMksb5ukebZA==", - "type": "package", - "path": "system.security.cryptography.algorithms/4.3.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Algorithms.dll", - "lib/net461/System.Security.Cryptography.Algorithms.dll", - "lib/net463/System.Security.Cryptography.Algorithms.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Algorithms.dll", - "ref/net461/System.Security.Cryptography.Algorithms.dll", - "ref/net463/System.Security.Cryptography.Algorithms.dll", - "ref/netstandard1.3/System.Security.Cryptography.Algorithms.dll", - "ref/netstandard1.4/System.Security.Cryptography.Algorithms.dll", - "ref/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/osx/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/net461/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/net463/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/netcore50/System.Security.Cryptography.Algorithms.dll", - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Algorithms.dll", - "system.security.cryptography.algorithms.4.3.1.nupkg.sha512", - "system.security.cryptography.algorithms.nuspec" - ] - }, - "System.Security.Cryptography.Cng/4.5.0": { - "sha512": "DsHSZoBeVe2bAknPwdrgnCQlgd80XRcS8V3ev952BF8ZE4jUi2i7+tIaFth6md9nure1SJL2hfQt4IgjpNybxQ==", - "type": "package", - "path": "system.security.cryptography.cng/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Cng.dll", - "lib/net461/System.Security.Cryptography.Cng.dll", - "lib/net462/System.Security.Cryptography.Cng.dll", - "lib/net47/System.Security.Cryptography.Cng.dll", - "lib/netcoreapp2.1/System.Security.Cryptography.Cng.dll", - "lib/netstandard1.3/System.Security.Cryptography.Cng.dll", - "lib/netstandard1.4/System.Security.Cryptography.Cng.dll", - "lib/netstandard1.6/System.Security.Cryptography.Cng.dll", - "lib/netstandard2.0/System.Security.Cryptography.Cng.dll", - "lib/uap10.0.16299/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Cng.dll", - "ref/net461/System.Security.Cryptography.Cng.dll", - "ref/net461/System.Security.Cryptography.Cng.xml", - "ref/net462/System.Security.Cryptography.Cng.dll", - "ref/net462/System.Security.Cryptography.Cng.xml", - "ref/net47/System.Security.Cryptography.Cng.dll", - "ref/net47/System.Security.Cryptography.Cng.xml", - "ref/netcoreapp2.0/System.Security.Cryptography.Cng.dll", - "ref/netcoreapp2.0/System.Security.Cryptography.Cng.xml", - "ref/netcoreapp2.1/System.Security.Cryptography.Cng.dll", - "ref/netcoreapp2.1/System.Security.Cryptography.Cng.xml", - "ref/netstandard1.3/System.Security.Cryptography.Cng.dll", - "ref/netstandard1.4/System.Security.Cryptography.Cng.dll", - "ref/netstandard1.6/System.Security.Cryptography.Cng.dll", - "ref/netstandard2.0/System.Security.Cryptography.Cng.dll", - "ref/netstandard2.0/System.Security.Cryptography.Cng.xml", - "ref/uap10.0.16299/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/win/lib/net46/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/net461/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/net462/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/net47/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/netcoreapp2.0/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/netstandard1.4/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.Cng.dll", - "runtimes/win/lib/uap10.0.16299/_._", - "system.security.cryptography.cng.4.5.0.nupkg.sha512", - "system.security.cryptography.cng.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Security.Cryptography.Csp/4.3.0": { - "sha512": "QzF1kXR6GPUvaDGH4Jrf4OA1c+baxDC/O6E/RAzbHHux+SBTadXzsqDz/flgTVuh5tlKiZol0sUz5FMzhXjzUQ==", - "type": "package", - "path": "system.security.cryptography.csp/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Csp.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Csp.dll", - "ref/netstandard1.3/System.Security.Cryptography.Csp.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Csp.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.Csp.dll", - "runtimes/win/lib/netcore50/_._", - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Csp.dll", - "system.security.cryptography.csp.4.3.0.nupkg.sha512", - "system.security.cryptography.csp.nuspec" - ] - }, - "System.Security.Cryptography.Encoding/4.3.0": { - "sha512": "XCat0j5jVC83UG9fofcuiYDwN0PVKc2OWD0QVLjYpXn7dz+gNaANkHPbhNtr5PR8rDQNHrxtI912Hb29YAB14A==", - "type": "package", - "path": "system.security.cryptography.encoding/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Encoding.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Encoding.dll", - "ref/netstandard1.3/System.Security.Cryptography.Encoding.dll", - "ref/netstandard1.3/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/de/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/es/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/fr/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/it/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/ja/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/ko/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/ru/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/zh-hans/System.Security.Cryptography.Encoding.xml", - "ref/netstandard1.3/zh-hant/System.Security.Cryptography.Encoding.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.Encoding.dll", - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Encoding.dll", - "system.security.cryptography.encoding.4.3.0.nupkg.sha512", - "system.security.cryptography.encoding.nuspec" - ] - }, - "System.Security.Cryptography.OpenSsl/4.3.0": { - "sha512": "ZFMKGUiXMPhz+MaOayRRNeomDALWhZGIAmF2g1jQFFeVEyul7od3QYIv8F3NDGHtyidpbvmej5MCohyt87Eynw==", - "type": "package", - "path": "system.security.cryptography.openssl/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", - "ref/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.OpenSsl.dll", - "system.security.cryptography.openssl.4.3.0.nupkg.sha512", - "system.security.cryptography.openssl.nuspec" - ] - }, - "System.Security.Cryptography.Pkcs/4.5.0": { - "sha512": "pPs9FSjvXyrpm545MneBUCGmjFVvrga+wJ1zSPV1fY3/R4w5NBGXperx2dY85YvoDvots+mbLwDAqNb/UR8Deg==", - "type": "package", - "path": "system.security.cryptography.pkcs/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net46/System.Security.Cryptography.Pkcs.dll", - "lib/net461/System.Security.Cryptography.Pkcs.dll", - "lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll", - "lib/netstandard1.3/System.Security.Cryptography.Pkcs.dll", - "lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll", - "ref/net46/System.Security.Cryptography.Pkcs.dll", - "ref/net461/System.Security.Cryptography.Pkcs.dll", - "ref/net461/System.Security.Cryptography.Pkcs.xml", - "ref/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll", - "ref/netcoreapp2.1/System.Security.Cryptography.Pkcs.xml", - "ref/netstandard1.3/System.Security.Cryptography.Pkcs.dll", - "ref/netstandard2.0/System.Security.Cryptography.Pkcs.dll", - "ref/netstandard2.0/System.Security.Cryptography.Pkcs.xml", - "runtimes/win/lib/net46/System.Security.Cryptography.Pkcs.dll", - "runtimes/win/lib/net461/System.Security.Cryptography.Pkcs.dll", - "runtimes/win/lib/netcoreapp2.1/System.Security.Cryptography.Pkcs.dll", - "runtimes/win/lib/netstandard1.3/System.Security.Cryptography.Pkcs.dll", - "runtimes/win/lib/netstandard2.0/System.Security.Cryptography.Pkcs.dll", - "system.security.cryptography.pkcs.4.5.0.nupkg.sha512", - "system.security.cryptography.pkcs.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Security.Cryptography.Primitives/4.3.0": { - "sha512": "WtgnP5mOu5zKL3vQMUPT9tV7XVYGV7Jtb0540DgBD7MMN5ojonwIcw8VybZvS6VloGmE7CRt/Hms8adBsN1DRw==", - "type": "package", - "path": "system.security.cryptography.primitives/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.Primitives.dll", - "lib/netstandard1.3/System.Security.Cryptography.Primitives.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.Primitives.dll", - "ref/netstandard1.3/System.Security.Cryptography.Primitives.dll", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.security.cryptography.primitives.4.3.0.nupkg.sha512", - "system.security.cryptography.primitives.nuspec" - ] - }, - "System.Security.Cryptography.X509Certificates/4.3.0": { - "sha512": "MY2Gq1Uo4rRE7D5LmCTBvoK7k9tRPqs0sjjkhviGxNdDEO2CwhEEAf5c15Dk2X9KAjoLLVwuKZUtP9AuQnNNAA==", - "type": "package", - "path": "system.security.cryptography.x509certificates/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Security.Cryptography.X509Certificates.dll", - "lib/net461/System.Security.Cryptography.X509Certificates.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Security.Cryptography.X509Certificates.dll", - "ref/net461/System.Security.Cryptography.X509Certificates.dll", - "ref/netstandard1.3/System.Security.Cryptography.X509Certificates.dll", - "ref/netstandard1.3/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/de/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/es/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/fr/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/it/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/ja/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/ko/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/ru/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/zh-hans/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.3/zh-hant/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.dll", - "ref/netstandard1.4/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/de/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/es/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/fr/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/it/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/ja/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/ko/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/ru/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/zh-hans/System.Security.Cryptography.X509Certificates.xml", - "ref/netstandard1.4/zh-hant/System.Security.Cryptography.X509Certificates.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/unix/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/net46/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/net461/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/netcore50/System.Security.Cryptography.X509Certificates.dll", - "runtimes/win/lib/netstandard1.6/System.Security.Cryptography.X509Certificates.dll", - "system.security.cryptography.x509certificates.4.3.0.nupkg.sha512", - "system.security.cryptography.x509certificates.nuspec" - ] - }, - "System.Security.Cryptography.Xml/4.5.0": { - "sha512": "X+/taLXTKYziKvWE4ZEyhpY0QQ17fm9eW70cvMCE6LAsWYqdv708i0HJeSVy7/5R5547GR/CEGOjUkYq6bJfPg==", - "type": "package", - "path": "system.security.cryptography.xml/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net461/System.Security.Cryptography.Xml.dll", - "lib/netstandard2.0/System.Security.Cryptography.Xml.dll", - "ref/net461/System.Security.Cryptography.Xml.dll", - "ref/net461/System.Security.Cryptography.Xml.xml", - "ref/netstandard2.0/System.Security.Cryptography.Xml.dll", - "ref/netstandard2.0/System.Security.Cryptography.Xml.xml", - "system.security.cryptography.xml.4.5.0.nupkg.sha512", - "system.security.cryptography.xml.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Security.Permissions/4.5.0": { - "sha512": "O+e9qamSTJ4YOJCpnLgsf9FTGfsxJv2On1OdYkhmd/XA5AYRvUatkz7Rp3dS9XR7rhVuklnjST1dRoMK7N4cGw==", - "type": "package", - "path": "system.security.permissions/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net461/System.Security.Permissions.dll", - "lib/netstandard2.0/System.Security.Permissions.dll", - "ref/net461/System.Security.Permissions.dll", - "ref/net461/System.Security.Permissions.xml", - "ref/netstandard2.0/System.Security.Permissions.dll", - "ref/netstandard2.0/System.Security.Permissions.xml", - "system.security.permissions.4.5.0.nupkg.sha512", - "system.security.permissions.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Security.Principal/4.3.0": { - "sha512": "24oe0NGJY32e+DFHVQzl2okM9uwYmn0Aa6nehqtVZ55/Al4Yva7S3BN934Kn5qATH7TVTUJkgxhisdfF7mKDfg==", - "type": "package", - "path": "system.security.principal/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Security.Principal.dll", - "lib/netstandard1.0/System.Security.Principal.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Security.Principal.dll", - "ref/netcore50/System.Security.Principal.xml", - "ref/netcore50/de/System.Security.Principal.xml", - "ref/netcore50/es/System.Security.Principal.xml", - "ref/netcore50/fr/System.Security.Principal.xml", - "ref/netcore50/it/System.Security.Principal.xml", - "ref/netcore50/ja/System.Security.Principal.xml", - "ref/netcore50/ko/System.Security.Principal.xml", - "ref/netcore50/ru/System.Security.Principal.xml", - "ref/netcore50/zh-hans/System.Security.Principal.xml", - "ref/netcore50/zh-hant/System.Security.Principal.xml", - "ref/netstandard1.0/System.Security.Principal.dll", - "ref/netstandard1.0/System.Security.Principal.xml", - "ref/netstandard1.0/de/System.Security.Principal.xml", - "ref/netstandard1.0/es/System.Security.Principal.xml", - "ref/netstandard1.0/fr/System.Security.Principal.xml", - "ref/netstandard1.0/it/System.Security.Principal.xml", - "ref/netstandard1.0/ja/System.Security.Principal.xml", - "ref/netstandard1.0/ko/System.Security.Principal.xml", - "ref/netstandard1.0/ru/System.Security.Principal.xml", - "ref/netstandard1.0/zh-hans/System.Security.Principal.xml", - "ref/netstandard1.0/zh-hant/System.Security.Principal.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.security.principal.4.3.0.nupkg.sha512", - "system.security.principal.nuspec" - ] - }, - "System.Security.Principal.Windows/4.5.0": { - "sha512": "hs2zF4tOQ3V4iQttVnLrnR/i8AOrrAgu2Gmp4/jNaE/+5hiZWDj20FK/m/OW3Itdi9XDvqf55WzHkiWYtOSUNg==", - "type": "package", - "path": "system.security.principal.windows/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/net46/System.Security.Principal.Windows.dll", - "lib/net461/System.Security.Principal.Windows.dll", - "lib/netstandard1.3/System.Security.Principal.Windows.dll", - "lib/netstandard2.0/System.Security.Principal.Windows.dll", - "lib/uap10.0.16299/_._", - "ref/net46/System.Security.Principal.Windows.dll", - "ref/net461/System.Security.Principal.Windows.dll", - "ref/net461/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/System.Security.Principal.Windows.dll", - "ref/netstandard1.3/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/de/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/es/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/fr/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/it/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/ja/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/ko/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/ru/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/zh-hans/System.Security.Principal.Windows.xml", - "ref/netstandard1.3/zh-hant/System.Security.Principal.Windows.xml", - "ref/netstandard2.0/System.Security.Principal.Windows.dll", - "ref/netstandard2.0/System.Security.Principal.Windows.xml", - "ref/uap10.0.16299/_._", - "runtimes/unix/lib/netcoreapp2.0/System.Security.Principal.Windows.dll", - "runtimes/win/lib/net46/System.Security.Principal.Windows.dll", - "runtimes/win/lib/net461/System.Security.Principal.Windows.dll", - "runtimes/win/lib/netcoreapp2.0/System.Security.Principal.Windows.dll", - "runtimes/win/lib/netstandard1.3/System.Security.Principal.Windows.dll", - "runtimes/win/lib/uap10.0.16299/_._", - "system.security.principal.windows.4.5.0.nupkg.sha512", - "system.security.principal.windows.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.ServiceModel.Primitives/4.5.3": { - "sha512": "Wc9Hgg4Cmqi416zvEgq2sW1YYCGuhwWzspDclJWlFZqY6EGhFUPZU+kVpl5z9kAgrSOQP7/Uiik+PtSQtmq+5A==", - "type": "package", - "path": "system.servicemodel.primitives/4.5.3", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.ServiceModel.Primitives.dll", - "lib/net461/System.ServiceModel.Primitives.dll", - "lib/netcore50/System.ServiceModel.Primitives.dll", - "lib/netstandard1.3/System.ServiceModel.Primitives.dll", - "lib/netstandard2.0/System.ServiceModel.Primitives.dll", - "lib/netstandard2.0/System.ServiceModel.dll", - "lib/portable-net45+win8+wp8/_._", - "lib/uap10.0.16300/System.ServiceModel.Primitives.dll", - "lib/uap10.0.16300/System.ServiceModel.dll", - "lib/win8/_._", - "lib/wp8/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.ServiceModel.Primitives.dll", - "ref/net461/System.ServiceModel.Primitives.dll", - "ref/netcore50/System.ServiceModel.Primitives.dll", - "ref/netstandard1.0/System.ServiceModel.Primitives.dll", - "ref/netstandard1.1/System.ServiceModel.Primitives.dll", - "ref/netstandard1.3/System.ServiceModel.Primitives.dll", - "ref/netstandard2.0/System.ServiceModel.Primitives.dll", - "ref/netstandard2.0/System.ServiceModel.dll", - "ref/portable-net45+win8+wp8/_._", - "ref/uap10.0.16300/System.ServiceModel.Primitives.dll", - "ref/uap10.0.16300/System.ServiceModel.dll", - "ref/win8/_._", - "ref/wp8/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.servicemodel.primitives.4.5.3.nupkg.sha512", - "system.servicemodel.primitives.nuspec", - "version.txt" - ] - }, - "System.Text.Encoding/4.3.0": { - "sha512": "b/f+7HMTpxIfeV7H03bkuHKMFylCGfr9/U6gePnfFFW0aF8LOWLDgQCY6V1oWUqDksC3mdNuyChM1vy9TP4sZw==", - "type": "package", - "path": "system.text.encoding/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Text.Encoding.dll", - "ref/netcore50/System.Text.Encoding.xml", - "ref/netcore50/de/System.Text.Encoding.xml", - "ref/netcore50/es/System.Text.Encoding.xml", - "ref/netcore50/fr/System.Text.Encoding.xml", - "ref/netcore50/it/System.Text.Encoding.xml", - "ref/netcore50/ja/System.Text.Encoding.xml", - "ref/netcore50/ko/System.Text.Encoding.xml", - "ref/netcore50/ru/System.Text.Encoding.xml", - "ref/netcore50/zh-hans/System.Text.Encoding.xml", - "ref/netcore50/zh-hant/System.Text.Encoding.xml", - "ref/netstandard1.0/System.Text.Encoding.dll", - "ref/netstandard1.0/System.Text.Encoding.xml", - "ref/netstandard1.0/de/System.Text.Encoding.xml", - "ref/netstandard1.0/es/System.Text.Encoding.xml", - "ref/netstandard1.0/fr/System.Text.Encoding.xml", - "ref/netstandard1.0/it/System.Text.Encoding.xml", - "ref/netstandard1.0/ja/System.Text.Encoding.xml", - "ref/netstandard1.0/ko/System.Text.Encoding.xml", - "ref/netstandard1.0/ru/System.Text.Encoding.xml", - "ref/netstandard1.0/zh-hans/System.Text.Encoding.xml", - "ref/netstandard1.0/zh-hant/System.Text.Encoding.xml", - "ref/netstandard1.3/System.Text.Encoding.dll", - "ref/netstandard1.3/System.Text.Encoding.xml", - "ref/netstandard1.3/de/System.Text.Encoding.xml", - "ref/netstandard1.3/es/System.Text.Encoding.xml", - "ref/netstandard1.3/fr/System.Text.Encoding.xml", - "ref/netstandard1.3/it/System.Text.Encoding.xml", - "ref/netstandard1.3/ja/System.Text.Encoding.xml", - "ref/netstandard1.3/ko/System.Text.Encoding.xml", - "ref/netstandard1.3/ru/System.Text.Encoding.xml", - "ref/netstandard1.3/zh-hans/System.Text.Encoding.xml", - "ref/netstandard1.3/zh-hant/System.Text.Encoding.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.text.encoding.4.3.0.nupkg.sha512", - "system.text.encoding.nuspec" - ] - }, - "System.Text.Encoding.CodePages/4.5.0": { - "sha512": "aQnlUgT+JK/81iv62zE7hR7FZJOgKbMNuxlK5l6u3CchtZ2FG5LIMnedmvVgT1phTnX4qWCUIoyfGTQlxrLNog==", - "type": "package", - "path": "system.text.encoding.codepages/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Text.Encoding.CodePages.dll", - "lib/net461/System.Text.Encoding.CodePages.dll", - "lib/netstandard1.3/System.Text.Encoding.CodePages.dll", - "lib/netstandard2.0/System.Text.Encoding.CodePages.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/netstandard1.3/System.Text.Encoding.CodePages.dll", - "ref/netstandard1.3/System.Text.Encoding.CodePages.xml", - "ref/netstandard1.3/de/System.Text.Encoding.CodePages.xml", - "ref/netstandard1.3/es/System.Text.Encoding.CodePages.xml", - "ref/netstandard1.3/fr/System.Text.Encoding.CodePages.xml", - "ref/netstandard1.3/it/System.Text.Encoding.CodePages.xml", - "ref/netstandard1.3/ja/System.Text.Encoding.CodePages.xml", - "ref/netstandard1.3/ko/System.Text.Encoding.CodePages.xml", - "ref/netstandard1.3/ru/System.Text.Encoding.CodePages.xml", - "ref/netstandard1.3/zh-hans/System.Text.Encoding.CodePages.xml", - "ref/netstandard1.3/zh-hant/System.Text.Encoding.CodePages.xml", - "ref/netstandard2.0/System.Text.Encoding.CodePages.dll", - "ref/netstandard2.0/System.Text.Encoding.CodePages.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/win/lib/net461/System.Text.Encoding.CodePages.dll", - "runtimes/win/lib/netcoreapp2.0/System.Text.Encoding.CodePages.dll", - "runtimes/win/lib/netstandard1.3/System.Text.Encoding.CodePages.dll", - "runtimes/win/lib/netstandard2.0/System.Text.Encoding.CodePages.dll", - "system.text.encoding.codepages.4.5.0.nupkg.sha512", - "system.text.encoding.codepages.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Text.Encoding.Extensions/4.3.0": { - "sha512": "5kjF3HgeNc8AxcyOfkLoFbljz4+3iOioF/m1PjGLK0Li96VW6cPGS/L2ov1GFfJqtPDU63E6AVHnHgrz/pw+7Q==", - "type": "package", - "path": "system.text.encoding.extensions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Text.Encoding.Extensions.dll", - "ref/netcore50/System.Text.Encoding.Extensions.xml", - "ref/netcore50/de/System.Text.Encoding.Extensions.xml", - "ref/netcore50/es/System.Text.Encoding.Extensions.xml", - "ref/netcore50/fr/System.Text.Encoding.Extensions.xml", - "ref/netcore50/it/System.Text.Encoding.Extensions.xml", - "ref/netcore50/ja/System.Text.Encoding.Extensions.xml", - "ref/netcore50/ko/System.Text.Encoding.Extensions.xml", - "ref/netcore50/ru/System.Text.Encoding.Extensions.xml", - "ref/netcore50/zh-hans/System.Text.Encoding.Extensions.xml", - "ref/netcore50/zh-hant/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/System.Text.Encoding.Extensions.dll", - "ref/netstandard1.0/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/de/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/es/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/fr/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/it/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/ja/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/ko/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/ru/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/zh-hans/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.0/zh-hant/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/System.Text.Encoding.Extensions.dll", - "ref/netstandard1.3/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/de/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/es/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/fr/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/it/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/ja/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/ko/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/ru/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/zh-hans/System.Text.Encoding.Extensions.xml", - "ref/netstandard1.3/zh-hant/System.Text.Encoding.Extensions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.text.encoding.extensions.4.3.0.nupkg.sha512", - "system.text.encoding.extensions.nuspec" - ] - }, - "System.Text.Encodings.Web/4.5.0": { - "sha512": "+AL7vP4ApfVSCSxphwM/fNeUp7ij7W/Gubc3jBK9wIG5SnzthpRHpKeTIutHRXlzukl9qgfGqUymQ4jPkoKieQ==", - "type": "package", - "path": "system.text.encodings.web/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Text.Encodings.Web.dll", - "lib/netstandard1.0/System.Text.Encodings.Web.xml", - "lib/netstandard2.0/System.Text.Encodings.Web.dll", - "lib/netstandard2.0/System.Text.Encodings.Web.xml", - "system.text.encodings.web.4.5.0.nupkg.sha512", - "system.text.encodings.web.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Text.RegularExpressions/4.3.0": { - "sha512": "gDU8FI3zDZosA+4QpiTZG2TXzMMhjLlmNEz6cGV/C1nIZ/7Sq5QFf2SrKBrZMYNT8lwjN1wA4TdrZYmuCnCq0w==", - "type": "package", - "path": "system.text.regularexpressions/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net463/System.Text.RegularExpressions.dll", - "lib/netcore50/System.Text.RegularExpressions.dll", - "lib/netstandard1.6/System.Text.RegularExpressions.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net463/System.Text.RegularExpressions.dll", - "ref/netcore50/System.Text.RegularExpressions.dll", - "ref/netcore50/System.Text.RegularExpressions.xml", - "ref/netcore50/de/System.Text.RegularExpressions.xml", - "ref/netcore50/es/System.Text.RegularExpressions.xml", - "ref/netcore50/fr/System.Text.RegularExpressions.xml", - "ref/netcore50/it/System.Text.RegularExpressions.xml", - "ref/netcore50/ja/System.Text.RegularExpressions.xml", - "ref/netcore50/ko/System.Text.RegularExpressions.xml", - "ref/netcore50/ru/System.Text.RegularExpressions.xml", - "ref/netcore50/zh-hans/System.Text.RegularExpressions.xml", - "ref/netcore50/zh-hant/System.Text.RegularExpressions.xml", - "ref/netcoreapp1.1/System.Text.RegularExpressions.dll", - "ref/netstandard1.0/System.Text.RegularExpressions.dll", - "ref/netstandard1.0/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/de/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/es/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/fr/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/it/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/ja/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/ko/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/ru/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/zh-hans/System.Text.RegularExpressions.xml", - "ref/netstandard1.0/zh-hant/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/System.Text.RegularExpressions.dll", - "ref/netstandard1.3/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/de/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/es/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/fr/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/it/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/ja/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/ko/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/ru/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/zh-hans/System.Text.RegularExpressions.xml", - "ref/netstandard1.3/zh-hant/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/System.Text.RegularExpressions.dll", - "ref/netstandard1.6/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/de/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/es/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/fr/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/it/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/ja/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/ko/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/ru/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/zh-hans/System.Text.RegularExpressions.xml", - "ref/netstandard1.6/zh-hant/System.Text.RegularExpressions.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.text.regularexpressions.4.3.0.nupkg.sha512", - "system.text.regularexpressions.nuspec" - ] - }, - "System.Threading/4.3.0": { - "sha512": "l6J1G9zmn6r5xU+DSp/Vxgx6eG+qUvQgdpgo28m1gEwfNyG6HqlF6h2ESDXZCYEPnngsmkTQ+q7MyyMMTNlaiA==", - "type": "package", - "path": "system.threading/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Threading.dll", - "lib/netstandard1.3/System.Threading.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Threading.dll", - "ref/netcore50/System.Threading.xml", - "ref/netcore50/de/System.Threading.xml", - "ref/netcore50/es/System.Threading.xml", - "ref/netcore50/fr/System.Threading.xml", - "ref/netcore50/it/System.Threading.xml", - "ref/netcore50/ja/System.Threading.xml", - "ref/netcore50/ko/System.Threading.xml", - "ref/netcore50/ru/System.Threading.xml", - "ref/netcore50/zh-hans/System.Threading.xml", - "ref/netcore50/zh-hant/System.Threading.xml", - "ref/netstandard1.0/System.Threading.dll", - "ref/netstandard1.0/System.Threading.xml", - "ref/netstandard1.0/de/System.Threading.xml", - "ref/netstandard1.0/es/System.Threading.xml", - "ref/netstandard1.0/fr/System.Threading.xml", - "ref/netstandard1.0/it/System.Threading.xml", - "ref/netstandard1.0/ja/System.Threading.xml", - "ref/netstandard1.0/ko/System.Threading.xml", - "ref/netstandard1.0/ru/System.Threading.xml", - "ref/netstandard1.0/zh-hans/System.Threading.xml", - "ref/netstandard1.0/zh-hant/System.Threading.xml", - "ref/netstandard1.3/System.Threading.dll", - "ref/netstandard1.3/System.Threading.xml", - "ref/netstandard1.3/de/System.Threading.xml", - "ref/netstandard1.3/es/System.Threading.xml", - "ref/netstandard1.3/fr/System.Threading.xml", - "ref/netstandard1.3/it/System.Threading.xml", - "ref/netstandard1.3/ja/System.Threading.xml", - "ref/netstandard1.3/ko/System.Threading.xml", - "ref/netstandard1.3/ru/System.Threading.xml", - "ref/netstandard1.3/zh-hans/System.Threading.xml", - "ref/netstandard1.3/zh-hant/System.Threading.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Threading.dll", - "system.threading.4.3.0.nupkg.sha512", - "system.threading.nuspec" - ] - }, - "System.Threading.Channels/4.5.0": { - "sha512": "61eqqmreCig10E4OhJ0/6VaoxR5DR0s+YcerLeP4I36Ua5I9F+AH4UVFfpccUnu2QWDqZvqCPhsMvOIZGlYSZA==", - "type": "package", - "path": "system.threading.channels/4.5.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netcoreapp2.1/System.Threading.Channels.dll", - "lib/netcoreapp2.1/System.Threading.Channels.xml", - "lib/netstandard1.3/System.Threading.Channels.dll", - "lib/netstandard1.3/System.Threading.Channels.xml", - "lib/netstandard2.0/System.Threading.Channels.dll", - "lib/netstandard2.0/System.Threading.Channels.xml", - "system.threading.channels.4.5.0.nupkg.sha512", - "system.threading.channels.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Threading.Overlapped/4.3.0": { - "sha512": "m3HQ2dPiX/DSTpf+yJt8B0c+SRvzfqAJKx+QDWi+VLhz8svLT23MVjEOHPF/KiSLeArKU/iHescrbLd3yVgyNg==", - "type": "package", - "path": "system.threading.overlapped/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/net46/System.Threading.Overlapped.dll", - "ref/net46/System.Threading.Overlapped.dll", - "ref/netstandard1.3/System.Threading.Overlapped.dll", - "ref/netstandard1.3/System.Threading.Overlapped.xml", - "ref/netstandard1.3/de/System.Threading.Overlapped.xml", - "ref/netstandard1.3/es/System.Threading.Overlapped.xml", - "ref/netstandard1.3/fr/System.Threading.Overlapped.xml", - "ref/netstandard1.3/it/System.Threading.Overlapped.xml", - "ref/netstandard1.3/ja/System.Threading.Overlapped.xml", - "ref/netstandard1.3/ko/System.Threading.Overlapped.xml", - "ref/netstandard1.3/ru/System.Threading.Overlapped.xml", - "ref/netstandard1.3/zh-hans/System.Threading.Overlapped.xml", - "ref/netstandard1.3/zh-hant/System.Threading.Overlapped.xml", - "runtimes/unix/lib/netstandard1.3/System.Threading.Overlapped.dll", - "runtimes/win/lib/net46/System.Threading.Overlapped.dll", - "runtimes/win/lib/netcore50/System.Threading.Overlapped.dll", - "runtimes/win/lib/netstandard1.3/System.Threading.Overlapped.dll", - "system.threading.overlapped.4.3.0.nupkg.sha512", - "system.threading.overlapped.nuspec" - ] - }, - "System.Threading.Tasks/4.3.0": { - "sha512": "fUiP+CyyCjs872OA8trl6p97qma/da1xGq3h4zAbJZk8zyaU4zyEfqW5vbkP80xG/Nimun1vlWBboMEk7XxdEw==", - "type": "package", - "path": "system.threading.tasks/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Threading.Tasks.dll", - "ref/netcore50/System.Threading.Tasks.xml", - "ref/netcore50/de/System.Threading.Tasks.xml", - "ref/netcore50/es/System.Threading.Tasks.xml", - "ref/netcore50/fr/System.Threading.Tasks.xml", - "ref/netcore50/it/System.Threading.Tasks.xml", - "ref/netcore50/ja/System.Threading.Tasks.xml", - "ref/netcore50/ko/System.Threading.Tasks.xml", - "ref/netcore50/ru/System.Threading.Tasks.xml", - "ref/netcore50/zh-hans/System.Threading.Tasks.xml", - "ref/netcore50/zh-hant/System.Threading.Tasks.xml", - "ref/netstandard1.0/System.Threading.Tasks.dll", - "ref/netstandard1.0/System.Threading.Tasks.xml", - "ref/netstandard1.0/de/System.Threading.Tasks.xml", - "ref/netstandard1.0/es/System.Threading.Tasks.xml", - "ref/netstandard1.0/fr/System.Threading.Tasks.xml", - "ref/netstandard1.0/it/System.Threading.Tasks.xml", - "ref/netstandard1.0/ja/System.Threading.Tasks.xml", - "ref/netstandard1.0/ko/System.Threading.Tasks.xml", - "ref/netstandard1.0/ru/System.Threading.Tasks.xml", - "ref/netstandard1.0/zh-hans/System.Threading.Tasks.xml", - "ref/netstandard1.0/zh-hant/System.Threading.Tasks.xml", - "ref/netstandard1.3/System.Threading.Tasks.dll", - "ref/netstandard1.3/System.Threading.Tasks.xml", - "ref/netstandard1.3/de/System.Threading.Tasks.xml", - "ref/netstandard1.3/es/System.Threading.Tasks.xml", - "ref/netstandard1.3/fr/System.Threading.Tasks.xml", - "ref/netstandard1.3/it/System.Threading.Tasks.xml", - "ref/netstandard1.3/ja/System.Threading.Tasks.xml", - "ref/netstandard1.3/ko/System.Threading.Tasks.xml", - "ref/netstandard1.3/ru/System.Threading.Tasks.xml", - "ref/netstandard1.3/zh-hans/System.Threading.Tasks.xml", - "ref/netstandard1.3/zh-hant/System.Threading.Tasks.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.tasks.4.3.0.nupkg.sha512", - "system.threading.tasks.nuspec" - ] - }, - "System.Threading.Tasks.Dataflow/4.9.0": { - "sha512": "dTS+3D/GtG2/Pvc3E5YzVvAa7aQJgLDlZDIzukMOJjYudVOQOUXEU68y6Zi3Nn/jqIeB5kOCwrGbQFAKHVzXEQ==", - "type": "package", - "path": "system.threading.tasks.dataflow/4.9.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netstandard1.0/System.Threading.Tasks.Dataflow.dll", - "lib/netstandard1.0/System.Threading.Tasks.Dataflow.xml", - "lib/netstandard1.1/System.Threading.Tasks.Dataflow.dll", - "lib/netstandard1.1/System.Threading.Tasks.Dataflow.xml", - "lib/netstandard2.0/System.Threading.Tasks.Dataflow.dll", - "lib/netstandard2.0/System.Threading.Tasks.Dataflow.xml", - "lib/portable-net45+win8+wpa81/System.Threading.Tasks.Dataflow.dll", - "lib/portable-net45+win8+wpa81/System.Threading.Tasks.Dataflow.xml", - "system.threading.tasks.dataflow.4.9.0.nupkg.sha512", - "system.threading.tasks.dataflow.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Threading.Tasks.Extensions/4.5.2": { - "sha512": "BG/TNxDFv0svAzx8OiMXDlsHfGw623BZ8tCXw4YLhDFDvDhNUEV58jKYMGRnkbJNm7c3JNNJDiN7JBMzxRBR2w==", - "type": "package", - "path": "system.threading.tasks.extensions/4.5.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/netcoreapp2.1/_._", - "lib/netstandard1.0/System.Threading.Tasks.Extensions.dll", - "lib/netstandard1.0/System.Threading.Tasks.Extensions.xml", - "lib/netstandard2.0/System.Threading.Tasks.Extensions.dll", - "lib/netstandard2.0/System.Threading.Tasks.Extensions.xml", - "lib/portable-net45+win8+wp8+wpa81/System.Threading.Tasks.Extensions.dll", - "lib/portable-net45+win8+wp8+wpa81/System.Threading.Tasks.Extensions.xml", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/netcoreapp2.1/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.tasks.extensions.4.5.2.nupkg.sha512", - "system.threading.tasks.extensions.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - }, - "System.Threading.Tasks.Parallel/4.3.0": { - "sha512": "Rg7sJJKyzI/I/vpk/xSNd6ri2hV8qrJdAwI81uIGTNjsKrP2j9ci++io3B4F53XSrs14mg/F1I/irlmSHtWhKg==", - "type": "package", - "path": "system.threading.tasks.parallel/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Threading.Tasks.Parallel.dll", - "lib/netstandard1.3/System.Threading.Tasks.Parallel.dll", - "lib/portable-net45+win8+wpa81/_._", - "lib/win8/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Threading.Tasks.Parallel.dll", - "ref/netcore50/System.Threading.Tasks.Parallel.xml", - "ref/netcore50/de/System.Threading.Tasks.Parallel.xml", - "ref/netcore50/es/System.Threading.Tasks.Parallel.xml", - "ref/netcore50/fr/System.Threading.Tasks.Parallel.xml", - "ref/netcore50/it/System.Threading.Tasks.Parallel.xml", - "ref/netcore50/ja/System.Threading.Tasks.Parallel.xml", - "ref/netcore50/ko/System.Threading.Tasks.Parallel.xml", - "ref/netcore50/ru/System.Threading.Tasks.Parallel.xml", - "ref/netcore50/zh-hans/System.Threading.Tasks.Parallel.xml", - "ref/netcore50/zh-hant/System.Threading.Tasks.Parallel.xml", - "ref/netstandard1.1/System.Threading.Tasks.Parallel.dll", - "ref/netstandard1.1/System.Threading.Tasks.Parallel.xml", - "ref/netstandard1.1/de/System.Threading.Tasks.Parallel.xml", - "ref/netstandard1.1/es/System.Threading.Tasks.Parallel.xml", - "ref/netstandard1.1/fr/System.Threading.Tasks.Parallel.xml", - "ref/netstandard1.1/it/System.Threading.Tasks.Parallel.xml", - "ref/netstandard1.1/ja/System.Threading.Tasks.Parallel.xml", - "ref/netstandard1.1/ko/System.Threading.Tasks.Parallel.xml", - "ref/netstandard1.1/ru/System.Threading.Tasks.Parallel.xml", - "ref/netstandard1.1/zh-hans/System.Threading.Tasks.Parallel.xml", - "ref/netstandard1.1/zh-hant/System.Threading.Tasks.Parallel.xml", - "ref/portable-net45+win8+wpa81/_._", - "ref/win8/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.tasks.parallel.4.3.0.nupkg.sha512", - "system.threading.tasks.parallel.nuspec" - ] - }, - "System.Threading.Thread/4.3.0": { - "sha512": "z+EramDnni9/yneaURFT1bDcrlnqGxFgb2Mn2/izxWXiVR6OytpVjmLdO2hLXJ1nZXUCUEjt+9OYj69/cjWl/g==", - "type": "package", - "path": "system.threading.thread/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Threading.Thread.dll", - "lib/netcore50/_._", - "lib/netstandard1.3/System.Threading.Thread.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Threading.Thread.dll", - "ref/netstandard1.3/System.Threading.Thread.dll", - "ref/netstandard1.3/System.Threading.Thread.xml", - "ref/netstandard1.3/de/System.Threading.Thread.xml", - "ref/netstandard1.3/es/System.Threading.Thread.xml", - "ref/netstandard1.3/fr/System.Threading.Thread.xml", - "ref/netstandard1.3/it/System.Threading.Thread.xml", - "ref/netstandard1.3/ja/System.Threading.Thread.xml", - "ref/netstandard1.3/ko/System.Threading.Thread.xml", - "ref/netstandard1.3/ru/System.Threading.Thread.xml", - "ref/netstandard1.3/zh-hans/System.Threading.Thread.xml", - "ref/netstandard1.3/zh-hant/System.Threading.Thread.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.thread.4.3.0.nupkg.sha512", - "system.threading.thread.nuspec" - ] - }, - "System.Threading.ThreadPool/4.3.0": { - "sha512": "RQpA+UpI6Tlpeedk5JStYk2DM/M3i5HqabI/yDbfj1xDu9bIz9kdoquVpHbh/wQjOJaOCbcgRH8iQcAUv8dRWQ==", - "type": "package", - "path": "system.threading.threadpool/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Threading.ThreadPool.dll", - "lib/netcore50/_._", - "lib/netstandard1.3/System.Threading.ThreadPool.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Threading.ThreadPool.dll", - "ref/netstandard1.3/System.Threading.ThreadPool.dll", - "ref/netstandard1.3/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/de/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/es/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/fr/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/it/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/ja/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/ko/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/ru/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/zh-hans/System.Threading.ThreadPool.xml", - "ref/netstandard1.3/zh-hant/System.Threading.ThreadPool.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.threadpool.4.3.0.nupkg.sha512", - "system.threading.threadpool.nuspec" - ] - }, - "System.Threading.Timer/4.3.0": { - "sha512": "1c6OJYt7574mj5ROIWIRlZSBBvV+bEbmmyiHxG9Wd2A2ixToQEa0vkRm7NCOzUywQBai/3lIy0ZAlgvvx6oXOQ==", - "type": "package", - "path": "system.threading.timer/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net451/_._", - "lib/portable-net451+win81+wpa81/_._", - "lib/win81/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net451/_._", - "ref/netcore50/System.Threading.Timer.dll", - "ref/netcore50/System.Threading.Timer.xml", - "ref/netcore50/de/System.Threading.Timer.xml", - "ref/netcore50/es/System.Threading.Timer.xml", - "ref/netcore50/fr/System.Threading.Timer.xml", - "ref/netcore50/it/System.Threading.Timer.xml", - "ref/netcore50/ja/System.Threading.Timer.xml", - "ref/netcore50/ko/System.Threading.Timer.xml", - "ref/netcore50/ru/System.Threading.Timer.xml", - "ref/netcore50/zh-hans/System.Threading.Timer.xml", - "ref/netcore50/zh-hant/System.Threading.Timer.xml", - "ref/netstandard1.2/System.Threading.Timer.dll", - "ref/netstandard1.2/System.Threading.Timer.xml", - "ref/netstandard1.2/de/System.Threading.Timer.xml", - "ref/netstandard1.2/es/System.Threading.Timer.xml", - "ref/netstandard1.2/fr/System.Threading.Timer.xml", - "ref/netstandard1.2/it/System.Threading.Timer.xml", - "ref/netstandard1.2/ja/System.Threading.Timer.xml", - "ref/netstandard1.2/ko/System.Threading.Timer.xml", - "ref/netstandard1.2/ru/System.Threading.Timer.xml", - "ref/netstandard1.2/zh-hans/System.Threading.Timer.xml", - "ref/netstandard1.2/zh-hant/System.Threading.Timer.xml", - "ref/portable-net451+win81+wpa81/_._", - "ref/win81/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.threading.timer.4.3.0.nupkg.sha512", - "system.threading.timer.nuspec" - ] - }, - "System.ValueTuple/4.3.0": { - "sha512": "vzTe8EMBRO8HwCdAWnzg2XksTAMe7Z9EWZbLxpBRl/owEM8oh8ei5czdhYT0od2LUVVd8hofbB98la0xAI5j1Q==", - "type": "package", - "path": "system.valuetuple/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/netstandard1.0/.xml", - "lib/netstandard1.0/System.ValueTuple.dll", - "lib/portable-net40+sl4+win8+wp8/.xml", - "lib/portable-net40+sl4+win8+wp8/System.ValueTuple.dll", - "system.valuetuple.4.3.0.nupkg.sha512", - "system.valuetuple.nuspec" - ] - }, - "System.Xml.ReaderWriter/4.3.0": { - "sha512": "mREBSX+9OeQ/wwbKKApGUxiGivqNsfNLuHwmb+YfDIGg7DSnl7I27oI71g0RSbdZLe+W/gRKu1EYWO//6JDC5g==", - "type": "package", - "path": "system.xml.readerwriter/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/net46/System.Xml.ReaderWriter.dll", - "lib/netcore50/System.Xml.ReaderWriter.dll", - "lib/netstandard1.3/System.Xml.ReaderWriter.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/net46/System.Xml.ReaderWriter.dll", - "ref/netcore50/System.Xml.ReaderWriter.dll", - "ref/netcore50/System.Xml.ReaderWriter.xml", - "ref/netcore50/de/System.Xml.ReaderWriter.xml", - "ref/netcore50/es/System.Xml.ReaderWriter.xml", - "ref/netcore50/fr/System.Xml.ReaderWriter.xml", - "ref/netcore50/it/System.Xml.ReaderWriter.xml", - "ref/netcore50/ja/System.Xml.ReaderWriter.xml", - "ref/netcore50/ko/System.Xml.ReaderWriter.xml", - "ref/netcore50/ru/System.Xml.ReaderWriter.xml", - "ref/netcore50/zh-hans/System.Xml.ReaderWriter.xml", - "ref/netcore50/zh-hant/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/System.Xml.ReaderWriter.dll", - "ref/netstandard1.0/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/de/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/es/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/fr/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/it/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/ja/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/ko/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/ru/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/zh-hans/System.Xml.ReaderWriter.xml", - "ref/netstandard1.0/zh-hant/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/System.Xml.ReaderWriter.dll", - "ref/netstandard1.3/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/de/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/es/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/fr/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/it/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/ja/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/ko/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/ru/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/zh-hans/System.Xml.ReaderWriter.xml", - "ref/netstandard1.3/zh-hant/System.Xml.ReaderWriter.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.xml.readerwriter.4.3.0.nupkg.sha512", - "system.xml.readerwriter.nuspec" - ] - }, - "System.Xml.XDocument/4.3.0": { - "sha512": "wtkjamltryOim1MLmqUQ+4EwQWhaG7mpWEWlHmHYcKBhXpiLFQ9b4NCJbvlLEj6X+WyKQ+6BXPW5iXWTmGsREw==", - "type": "package", - "path": "system.xml.xdocument/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Xml.XDocument.dll", - "lib/netstandard1.3/System.Xml.XDocument.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Xml.XDocument.dll", - "ref/netcore50/System.Xml.XDocument.xml", - "ref/netcore50/de/System.Xml.XDocument.xml", - "ref/netcore50/es/System.Xml.XDocument.xml", - "ref/netcore50/fr/System.Xml.XDocument.xml", - "ref/netcore50/it/System.Xml.XDocument.xml", - "ref/netcore50/ja/System.Xml.XDocument.xml", - "ref/netcore50/ko/System.Xml.XDocument.xml", - "ref/netcore50/ru/System.Xml.XDocument.xml", - "ref/netcore50/zh-hans/System.Xml.XDocument.xml", - "ref/netcore50/zh-hant/System.Xml.XDocument.xml", - "ref/netstandard1.0/System.Xml.XDocument.dll", - "ref/netstandard1.0/System.Xml.XDocument.xml", - "ref/netstandard1.0/de/System.Xml.XDocument.xml", - "ref/netstandard1.0/es/System.Xml.XDocument.xml", - "ref/netstandard1.0/fr/System.Xml.XDocument.xml", - "ref/netstandard1.0/it/System.Xml.XDocument.xml", - "ref/netstandard1.0/ja/System.Xml.XDocument.xml", - "ref/netstandard1.0/ko/System.Xml.XDocument.xml", - "ref/netstandard1.0/ru/System.Xml.XDocument.xml", - "ref/netstandard1.0/zh-hans/System.Xml.XDocument.xml", - "ref/netstandard1.0/zh-hant/System.Xml.XDocument.xml", - "ref/netstandard1.3/System.Xml.XDocument.dll", - "ref/netstandard1.3/System.Xml.XDocument.xml", - "ref/netstandard1.3/de/System.Xml.XDocument.xml", - "ref/netstandard1.3/es/System.Xml.XDocument.xml", - "ref/netstandard1.3/fr/System.Xml.XDocument.xml", - "ref/netstandard1.3/it/System.Xml.XDocument.xml", - "ref/netstandard1.3/ja/System.Xml.XDocument.xml", - "ref/netstandard1.3/ko/System.Xml.XDocument.xml", - "ref/netstandard1.3/ru/System.Xml.XDocument.xml", - "ref/netstandard1.3/zh-hans/System.Xml.XDocument.xml", - "ref/netstandard1.3/zh-hant/System.Xml.XDocument.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.xml.xdocument.4.3.0.nupkg.sha512", - "system.xml.xdocument.nuspec" - ] - }, - "System.Xml.XmlDocument/4.3.0": { - "sha512": "IiUbPxbemqBuCRskuuobjJV1Lw0iJm+vNOH7drNHsj95EM2vVnBY4j0GtweZYQkMpwgFBwokka3V2k0Cca/RMw==", - "type": "package", - "path": "system.xml.xmldocument/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Xml.XmlDocument.dll", - "lib/netstandard1.3/System.Xml.XmlDocument.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Xml.XmlDocument.dll", - "ref/netstandard1.3/System.Xml.XmlDocument.dll", - "ref/netstandard1.3/System.Xml.XmlDocument.xml", - "ref/netstandard1.3/de/System.Xml.XmlDocument.xml", - "ref/netstandard1.3/es/System.Xml.XmlDocument.xml", - "ref/netstandard1.3/fr/System.Xml.XmlDocument.xml", - "ref/netstandard1.3/it/System.Xml.XmlDocument.xml", - "ref/netstandard1.3/ja/System.Xml.XmlDocument.xml", - "ref/netstandard1.3/ko/System.Xml.XmlDocument.xml", - "ref/netstandard1.3/ru/System.Xml.XmlDocument.xml", - "ref/netstandard1.3/zh-hans/System.Xml.XmlDocument.xml", - "ref/netstandard1.3/zh-hant/System.Xml.XmlDocument.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.xml.xmldocument.4.3.0.nupkg.sha512", - "system.xml.xmldocument.nuspec" - ] - }, - "System.Xml.XmlSerializer/4.3.0": { - "sha512": "kwo6Kf+0jjEcmB+S2BI5SL0xHHRIBd0Cjf11BGliuZ3Va9V1hDbKZmq0mb7HO8QPlYYrq8eNToOzipfw6ARKEw==", - "type": "package", - "path": "system.xml.xmlserializer/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net45/_._", - "lib/netcore50/System.Xml.XmlSerializer.dll", - "lib/netstandard1.3/System.Xml.XmlSerializer.dll", - "lib/portable-net45+win8+wp8+wpa81/_._", - "lib/win8/_._", - "lib/wp80/_._", - "lib/wpa81/_._", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net45/_._", - "ref/netcore50/System.Xml.XmlSerializer.dll", - "ref/netcore50/System.Xml.XmlSerializer.xml", - "ref/netcore50/de/System.Xml.XmlSerializer.xml", - "ref/netcore50/es/System.Xml.XmlSerializer.xml", - "ref/netcore50/fr/System.Xml.XmlSerializer.xml", - "ref/netcore50/it/System.Xml.XmlSerializer.xml", - "ref/netcore50/ja/System.Xml.XmlSerializer.xml", - "ref/netcore50/ko/System.Xml.XmlSerializer.xml", - "ref/netcore50/ru/System.Xml.XmlSerializer.xml", - "ref/netcore50/zh-hans/System.Xml.XmlSerializer.xml", - "ref/netcore50/zh-hant/System.Xml.XmlSerializer.xml", - "ref/netstandard1.0/System.Xml.XmlSerializer.dll", - "ref/netstandard1.0/System.Xml.XmlSerializer.xml", - "ref/netstandard1.0/de/System.Xml.XmlSerializer.xml", - "ref/netstandard1.0/es/System.Xml.XmlSerializer.xml", - "ref/netstandard1.0/fr/System.Xml.XmlSerializer.xml", - "ref/netstandard1.0/it/System.Xml.XmlSerializer.xml", - "ref/netstandard1.0/ja/System.Xml.XmlSerializer.xml", - "ref/netstandard1.0/ko/System.Xml.XmlSerializer.xml", - "ref/netstandard1.0/ru/System.Xml.XmlSerializer.xml", - "ref/netstandard1.0/zh-hans/System.Xml.XmlSerializer.xml", - "ref/netstandard1.0/zh-hant/System.Xml.XmlSerializer.xml", - "ref/netstandard1.3/System.Xml.XmlSerializer.dll", - "ref/netstandard1.3/System.Xml.XmlSerializer.xml", - "ref/netstandard1.3/de/System.Xml.XmlSerializer.xml", - "ref/netstandard1.3/es/System.Xml.XmlSerializer.xml", - "ref/netstandard1.3/fr/System.Xml.XmlSerializer.xml", - "ref/netstandard1.3/it/System.Xml.XmlSerializer.xml", - "ref/netstandard1.3/ja/System.Xml.XmlSerializer.xml", - "ref/netstandard1.3/ko/System.Xml.XmlSerializer.xml", - "ref/netstandard1.3/ru/System.Xml.XmlSerializer.xml", - "ref/netstandard1.3/zh-hans/System.Xml.XmlSerializer.xml", - "ref/netstandard1.3/zh-hant/System.Xml.XmlSerializer.xml", - "ref/portable-net45+win8+wp8+wpa81/_._", - "ref/win8/_._", - "ref/wp80/_._", - "ref/wpa81/_._", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "runtimes/aot/lib/netcore50/System.Xml.XmlSerializer.dll", - "system.xml.xmlserializer.4.3.0.nupkg.sha512", - "system.xml.xmlserializer.nuspec" - ] - }, - "System.Xml.XPath/4.3.0": { - "sha512": "K9CXmiAgV5+zKtN0uJkponLnY+4SM+2aIVQIwKH5+Cpf23QzDBxVphAuDf24t7We0l/fZFS/PeFGFS6nefNm4A==", - "type": "package", - "path": "system.xml.xpath/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Xml.XPath.dll", - "lib/netstandard1.3/System.Xml.XPath.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Xml.XPath.dll", - "ref/netstandard1.3/System.Xml.XPath.dll", - "ref/netstandard1.3/System.Xml.XPath.xml", - "ref/netstandard1.3/de/System.Xml.XPath.xml", - "ref/netstandard1.3/es/System.Xml.XPath.xml", - "ref/netstandard1.3/fr/System.Xml.XPath.xml", - "ref/netstandard1.3/it/System.Xml.XPath.xml", - "ref/netstandard1.3/ja/System.Xml.XPath.xml", - "ref/netstandard1.3/ko/System.Xml.XPath.xml", - "ref/netstandard1.3/ru/System.Xml.XPath.xml", - "ref/netstandard1.3/zh-hans/System.Xml.XPath.xml", - "ref/netstandard1.3/zh-hant/System.Xml.XPath.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.xml.xpath.4.3.0.nupkg.sha512", - "system.xml.xpath.nuspec" - ] - }, - "System.Xml.XPath.XDocument/4.3.0": { - "sha512": "BqyXhrLyCxM28/VqPJdTaQfQM/GpuewHJnGS9g7VFzKSSqNm9IHoYvH2PXX6vcXeUbRFxctsNvH7iWkKKbbh6A==", - "type": "package", - "path": "system.xml.xpath.xdocument/4.3.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ThirdPartyNotices.txt", - "dotnet_library_license.txt", - "lib/MonoAndroid10/_._", - "lib/MonoTouch10/_._", - "lib/net46/System.Xml.XPath.XDocument.dll", - "lib/netstandard1.3/System.Xml.XPath.XDocument.dll", - "lib/xamarinios10/_._", - "lib/xamarinmac20/_._", - "lib/xamarintvos10/_._", - "lib/xamarinwatchos10/_._", - "ref/MonoAndroid10/_._", - "ref/MonoTouch10/_._", - "ref/net46/System.Xml.XPath.XDocument.dll", - "ref/netstandard1.3/System.Xml.XPath.XDocument.dll", - "ref/netstandard1.3/System.Xml.XPath.XDocument.xml", - "ref/netstandard1.3/de/System.Xml.XPath.XDocument.xml", - "ref/netstandard1.3/es/System.Xml.XPath.XDocument.xml", - "ref/netstandard1.3/fr/System.Xml.XPath.XDocument.xml", - "ref/netstandard1.3/it/System.Xml.XPath.XDocument.xml", - "ref/netstandard1.3/ja/System.Xml.XPath.XDocument.xml", - "ref/netstandard1.3/ko/System.Xml.XPath.XDocument.xml", - "ref/netstandard1.3/ru/System.Xml.XPath.XDocument.xml", - "ref/netstandard1.3/zh-hans/System.Xml.XPath.XDocument.xml", - "ref/netstandard1.3/zh-hant/System.Xml.XPath.XDocument.xml", - "ref/xamarinios10/_._", - "ref/xamarinmac20/_._", - "ref/xamarintvos10/_._", - "ref/xamarinwatchos10/_._", - "system.xml.xpath.xdocument.4.3.0.nupkg.sha512", - "system.xml.xpath.xdocument.nuspec" - ] - }, - "Tmds.LibC/0.2.0": { - "sha512": "+RvLuNHOLW7cxzgDe9yHLoayBgjsuH2/gJtJnuVMxweKrxxYT6TwQNAmt06SFWpjwk68aRcwwD4FfMMA6tZvVA==", - "type": "package", - "path": "tmds.libc/0.2.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "ref/netstandard2.0/Tmds.LibC.dll", - "runtimes/linux-arm/lib/netstandard2.0/Tmds.LibC.dll", - "runtimes/linux-arm64/lib/netstandard2.0/Tmds.LibC.dll", - "runtimes/linux-x64/lib/netstandard2.0/Tmds.LibC.dll", - "tmds.libc.0.2.0.nupkg.sha512", - "tmds.libc.nuspec" - ] - }, - "Catalyst.Abstractions/0.1.0": { - "type": "project", - "path": "../Catalyst.Abstractions/Catalyst.Abstractions.csproj", - "msbuildProject": "../Catalyst.Abstractions/Catalyst.Abstractions.csproj" - }, - "Catalyst.Core.Lib/1.0.0": { - "type": "project", - "path": "../Catalyst.Core.Lib/Catalyst.Core.Lib.csproj", - "msbuildProject": "../Catalyst.Core.Lib/Catalyst.Core.Lib.csproj" - }, - "Catalyst.Protocol/1.0.0": { - "type": "project", - "path": "../Catalyst.Protocol/Catalyst.Protocol.csproj", - "msbuildProject": "../Catalyst.Protocol/Catalyst.Protocol.csproj" - }, - "Nethermind.Core/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.Core/Nethermind.Core.csproj" - }, - "Nethermind.Dirichlet.Numerics/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Dirichlet/Nethermind.Dirichlet.Numerics/Nethermind.Dirichlet.Numerics.csproj", - "msbuildProject": "../../submodules/nethermind/src/Dirichlet/Nethermind.Dirichlet.Numerics/Nethermind.Dirichlet.Numerics.csproj" - }, - "Nethermind.Evm/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.Evm/Nethermind.Evm.csproj" - }, - "Nethermind.HashLib/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.HashLib/Nethermind.HashLib.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.HashLib/Nethermind.HashLib.csproj" - }, - "Nethermind.Logging/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.Logging/Nethermind.Logging.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.Logging/Nethermind.Logging.csproj" - }, - "Nethermind.Secp256k1/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.Secp256k1/Nethermind.Secp256k1.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.Secp256k1/Nethermind.Secp256k1.csproj" - }, - "Nethermind.Store/1.0.0": { - "type": "project", - "path": "../../submodules/nethermind/src/Nethermind/Nethermind.Store/Nethermind.Store.csproj", - "msbuildProject": "../../submodules/nethermind/src/Nethermind/Nethermind.Store/Nethermind.Store.csproj" - } - }, - "projectFileDependencyGroups": { - ".NETCoreApp,Version=v2.2": [ - "Autofac.Extensions.DependencyInjection >= 4.3.1", - "AutofacSerilogIntegration >= 2.0.0", - "Catalyst.Abstractions >= 0.1.0", - "Catalyst.Core.Lib >= 1.0.0", - "Google.Protobuf >= 3.9.1", - "Microsoft.AspNetCore >= 2.2.0", - "Microsoft.AspNetCore.App", - "Microsoft.AspNetCore.Mvc >= 2.2.0", - "Microsoft.AspNetCore.Mvc.Abstractions >= 2.2.0", - "Microsoft.AspNetCore.Mvc.Core >= 2.2.5", - "Microsoft.AspNetCore.Mvc.ViewFeatures >= 2.2.0", - "Microsoft.AspNetCore.StaticFiles >= 2.2.0", - "Serilog.AspNetCore >= 2.1.1", - "Swashbuckle.AspNetCore.Swagger >= 4.0.1", - "Swashbuckle.AspNetCore.SwaggerGen >= 4.0.1", - "Swashbuckle.AspNetCore.SwaggerUI >= 4.0.1" - ] - }, - "packageFolders": { - "/home/nsh/.nuget/packages/": {} - }, - "project": { - "version": "1.0.0", - "restore": { - "projectUniqueName": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj", - "projectName": "Catalyst.Core.Modules.Web3", - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj", - "packagesPath": "/home/nsh/.nuget/packages/", - "outputPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Modules.Web3/", - "projectStyle": "PackageReference", - "configFilePaths": [ - "/home/nsh/RiderProjects/Catalyst.Node/src/nuget.config", - "/home/nsh/.config/NuGet/NuGet.Config" - ], - "originalTargetFrameworks": [ - "netcoreapp2.2" - ], - "sources": { - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "netcoreapp2.2": { - "projectReferences": { - "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj": { - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj" - }, - "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj": { - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj" - } - } - } - } - }, - "frameworks": { - "netcoreapp2.2": { - "dependencies": { - "Autofac.Extensions.DependencyInjection": { - "target": "Package", - "version": "[4.3.1, )" - }, - "AutofacSerilogIntegration": { - "target": "Package", - "version": "[2.0.0, )" - }, - "Google.Protobuf": { - "target": "Package", - "version": "[3.9.1, )" - }, - "Microsoft.AspNetCore": { - "target": "Package", - "version": "[2.2.0, )" - }, - "Microsoft.AspNetCore.App": { - "target": "Package", - "version": "(, )" - }, - "Microsoft.AspNetCore.Mvc": { - "target": "Package", - "version": "[2.2.0, )" - }, - "Microsoft.AspNetCore.Mvc.Abstractions": { - "target": "Package", - "version": "[2.2.0, )" - }, - "Microsoft.AspNetCore.Mvc.Core": { - "target": "Package", - "version": "[2.2.5, )" - }, - "Microsoft.AspNetCore.Mvc.ViewFeatures": { - "target": "Package", - "version": "[2.2.0, )" - }, - "Microsoft.AspNetCore.StaticFiles": { - "target": "Package", - "version": "[2.2.0, )" - }, - "Serilog.AspNetCore": { - "target": "Package", - "version": "[2.1.1, )" - }, - "Swashbuckle.AspNetCore.Swagger": { - "target": "Package", - "version": "[4.0.1, )" - }, - "Swashbuckle.AspNetCore.SwaggerGen": { - "target": "Package", - "version": "[4.0.1, )" - }, - "Swashbuckle.AspNetCore.SwaggerUI": { - "target": "Package", - "version": "[4.0.1, )" - } - } - } - } - }, - "logs": [ - { - "code": "NU1604", - "level": "Warning", - "warningLevel": 1, - "message": "Project dependency Microsoft.AspNetCore.App does not contain an inclusive lower bound. Include a lower bound in the dependency version to ensure consistent restore results.", - "libraryId": "Microsoft.AspNetCore.App", - "targetGraphs": [ - ".NETCoreApp,Version=v2.2" - ] - }, - { - "code": "NU1608", - "level": "Warning", - "warningLevel": 1, - "message": "Detected package version outside of dependency constraint: Microsoft.AspNetCore.App 2.1.0 requires Microsoft.AspNetCore (= 2.1.0) but version Microsoft.AspNetCore 2.2.0 was resolved.", - "libraryId": "Microsoft.AspNetCore", - "targetGraphs": [ - ".NETCoreApp,Version=v2.2" - ] - }, - { - "code": "NU1608", - "level": "Warning", - "warningLevel": 1, - "message": "Detected package version outside of dependency constraint: Microsoft.AspNetCore.App 2.1.0 requires Microsoft.AspNetCore.Mvc (= 2.1.0) but version Microsoft.AspNetCore.Mvc 2.2.0 was resolved.", - "libraryId": "Microsoft.AspNetCore.Mvc", - "targetGraphs": [ - ".NETCoreApp,Version=v2.2" - ] - }, - { - "code": "NU1608", - "level": "Warning", - "warningLevel": 1, - "message": "Detected package version outside of dependency constraint: Microsoft.AspNetCore.App 2.1.0 requires Microsoft.AspNetCore.Mvc.Abstractions (= 2.1.0) but version Microsoft.AspNetCore.Mvc.Abstractions 2.2.0 was resolved.", - "libraryId": "Microsoft.AspNetCore.Mvc.Abstractions", - "targetGraphs": [ - ".NETCoreApp,Version=v2.2" - ] - }, - { - "code": "NU1608", - "level": "Warning", - "warningLevel": 1, - "message": "Detected package version outside of dependency constraint: Microsoft.AspNetCore.App 2.1.0 requires Microsoft.AspNetCore.Mvc.Core (= 2.1.0) but version Microsoft.AspNetCore.Mvc.Core 2.2.5 was resolved.", - "libraryId": "Microsoft.AspNetCore.Mvc.Core", - "targetGraphs": [ - ".NETCoreApp,Version=v2.2" - ] - }, - { - "code": "NU1608", - "level": "Warning", - "warningLevel": 1, - "message": "Detected package version outside of dependency constraint: Microsoft.AspNetCore.App 2.1.0 requires Microsoft.AspNetCore.Mvc.ViewFeatures (= 2.1.0) but version Microsoft.AspNetCore.Mvc.ViewFeatures 2.2.0 was resolved.", - "libraryId": "Microsoft.AspNetCore.Mvc.ViewFeatures", - "targetGraphs": [ - ".NETCoreApp,Version=v2.2" - ] - }, - { - "code": "NU1608", - "level": "Warning", - "warningLevel": 1, - "message": "Detected package version outside of dependency constraint: Microsoft.AspNetCore.App 2.1.0 requires Microsoft.AspNetCore.StaticFiles (= 2.1.0) but version Microsoft.AspNetCore.StaticFiles 2.2.0 was resolved.", - "libraryId": "Microsoft.AspNetCore.StaticFiles", - "targetGraphs": [ - ".NETCoreApp,Version=v2.2" - ] - }, - { - "code": "NU1107", - "level": "Error", - "message": "Version conflict detected for Microsoft.AspNetCore.Antiforgery. Install/reference Microsoft.AspNetCore.Antiforgery 2.2.0 directly to project Catalyst.Core.Modules.Web3 to resolve this issue. \n Catalyst.Core.Modules.Web3 -> Microsoft.AspNetCore.Mvc.ViewFeatures 2.2.0 -> Microsoft.AspNetCore.Antiforgery (>= 2.2.0) \n Catalyst.Core.Modules.Web3 -> Microsoft.AspNetCore.App 2.1.0 -> Microsoft.AspNetCore.Antiforgery (= 2.1.0).", - "libraryId": "Microsoft.AspNetCore.Antiforgery", - "targetGraphs": [ - ".NETCoreApp,Version=v2.2" - ] - } - ] -} \ No newline at end of file diff --git a/src/Catalyst.Core.Modules.Web3/project.packagespec.json b/src/Catalyst.Core.Modules.Web3/project.packagespec.json deleted file mode 100644 index ff8dda75ce..0000000000 --- a/src/Catalyst.Core.Modules.Web3/project.packagespec.json +++ /dev/null @@ -1,90 +0,0 @@ -{ - "version": "1.0.0", - "restore": { - "projectUniqueName": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj", - "projectName": "Catalyst.Core.Modules.Web3", - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Modules.Web3/Catalyst.Core.Modules.Web3.csproj", - "outputPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Modules.Web3/", - "projectStyle": "PackageReference", - "originalTargetFrameworks": [ - "netcoreapp2.2" - ], - "sources": { - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "netcoreapp2.2": { - "projectReferences": { - "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj": { - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Abstractions/Catalyst.Abstractions.csproj" - }, - "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj": { - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Core.Lib/Catalyst.Core.Lib.csproj" - } - } - } - } - }, - "frameworks": { - "netcoreapp2.2": { - "dependencies": { - "Autofac.Extensions.DependencyInjection": { - "target": "Package", - "version": "[4.3.1, )" - }, - "AutofacSerilogIntegration": { - "target": "Package", - "version": "[2.0.0, )" - }, - "Google.Protobuf": { - "target": "Package", - "version": "[3.9.1, )" - }, - "Microsoft.AspNetCore": { - "target": "Package", - "version": "[2.2.0, )" - }, - "Microsoft.AspNetCore.App": { - "target": "Package", - "version": "(, )" - }, - "Microsoft.AspNetCore.Mvc": { - "target": "Package", - "version": "[2.2.0, )" - }, - "Microsoft.AspNetCore.Mvc.Abstractions": { - "target": "Package", - "version": "[2.2.0, )" - }, - "Microsoft.AspNetCore.Mvc.Core": { - "target": "Package", - "version": "[2.2.5, )" - }, - "Microsoft.AspNetCore.Mvc.ViewFeatures": { - "target": "Package", - "version": "[2.2.0, )" - }, - "Microsoft.AspNetCore.StaticFiles": { - "target": "Package", - "version": "[2.2.0, )" - }, - "Serilog.AspNetCore": { - "target": "Package", - "version": "[2.1.1, )" - }, - "Swashbuckle.AspNetCore.Swagger": { - "target": "Package", - "version": "[4.0.1, )" - }, - "Swashbuckle.AspNetCore.SwaggerGen": { - "target": "Package", - "version": "[4.0.1, )" - }, - "Swashbuckle.AspNetCore.SwaggerUI": { - "target": "Package", - "version": "[4.0.1, )" - } - } - } - } -} \ No newline at end of file diff --git a/src/Catalyst.KBucket.Tests/Catalyst.KBucket.Tests.csproj b/src/Catalyst.KBucket.Tests/Catalyst.KBucket.Tests.csproj new file mode 100644 index 0000000000..521513517c --- /dev/null +++ b/src/Catalyst.KBucket.Tests/Catalyst.KBucket.Tests.csproj @@ -0,0 +1,23 @@ + + + + net6.0 + + false + full + Catalyst.KBucket + + + + + + + + + + + + + + + diff --git a/src/Catalyst.KBucket.Tests/ClosestTest.cs b/src/Catalyst.KBucket.Tests/ClosestTest.cs new file mode 100644 index 0000000000..7dab7d5984 --- /dev/null +++ b/src/Catalyst.KBucket.Tests/ClosestTest.cs @@ -0,0 +1,126 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Catalyst.KBucket +{ + /// + /// From https://github.com/tristanls/k-bucket/blob/master/test/closest.js + /// + [TestClass] + public class ClosestTest + { + [TestMethod] + public void ClosestNodes() + { + KBucket kBucket = new(); + for (var i = 0; i < 0x12; ++i) kBucket.Add(new Contact((byte) i)); + + Contact contact = new((byte) 0x15); // 00010101 + var contacts = kBucket.Closest(contact).Take(3).ToArray(); + CollectionAssert.AreEqual(new byte[] + { + 0x11 + }, contacts[0].Id); // distance: 00000100 + CollectionAssert.AreEqual(new byte[] + { + 0x10 + }, contacts[1].Id); // distance: 00000101 + CollectionAssert.AreEqual(new byte[] + { + 0x05 + }, contacts[2].Id); // distance: 00010000 + } + + [TestMethod] + public void All() + { + var kBucket = new KBucket + { + LocalContactId = new byte[] {0, 0} + }; + for (var i = 0; i < 1000; ++i) kBucket.Add(new Contact((byte) (i / 256), (byte) (i % 256))); + + Contact contact = new((byte) 0x80, (byte) 0x80); + var contacts = kBucket.Closest(contact); + Assert.IsTrue(contacts.Count() > 100); + } + + [TestMethod] + public void ClosestNodes_ExactMatch() + { + KBucket kBucket = new(); + for (var i = 0; i < 0x12; ++i) kBucket.Add(new Contact((byte) i)); + + Contact contact = new((byte) 0x11); // 00010001 + var contacts = kBucket.Closest(contact).Take(3).ToArray(); + CollectionAssert.AreEqual(new byte[] {0x11}, contacts[0].Id); // distance: 00000000 + CollectionAssert.AreEqual(new byte[] {0x10}, contacts[1].Id); // distance: 00000001 + CollectionAssert.AreEqual(new byte[] {0x01}, contacts[2].Id); // distance: 00010000 + } + + [TestMethod] + public void ClosestNodes_PartialBuckets() + { + var kBucket = new KBucket + { + LocalContactId = new byte[] {0, 0} + }; + for (var i = 0; i < kBucket.ContactsPerBucket; ++i) + { + kBucket.Add(new Contact((byte) 0x80, (byte) i)); + kBucket.Add(new Contact((byte) 0x01, (byte) i)); + } + + kBucket.Add(new Contact((byte) 0x00, (byte) 0x01)); + + Contact contact = new((byte) 0x00, (byte) 0x03); + var contacts = kBucket.Closest(contact).Take(22).ToArray(); + + CollectionAssert.AreEqual(contacts[0].Id, new byte[] {0x00, 0x01}); // distance: 0000000000000010 + CollectionAssert.AreEqual(contacts[1].Id, new byte[] {0x01, 0x03}); // distance: 0000000100000000 + CollectionAssert.AreEqual(contacts[2].Id, new byte[] {0x01, 0x02}); // distance: 0000000100000010 + CollectionAssert.AreEqual(contacts[3].Id, new byte[] {0x01, 0x01}); + CollectionAssert.AreEqual(contacts[4].Id, new byte[] {0x01, 0x00}); + CollectionAssert.AreEqual(contacts[5].Id, new byte[] {0x01, 0x07}); + CollectionAssert.AreEqual(contacts[6].Id, new byte[] {0x01, 0x06}); + CollectionAssert.AreEqual(contacts[7].Id, new byte[] {0x01, 0x05}); + CollectionAssert.AreEqual(contacts[8].Id, new byte[] {0x01, 0x04}); + CollectionAssert.AreEqual(contacts[9].Id, new byte[] {0x01, 0x0b}); + CollectionAssert.AreEqual(contacts[10].Id, new byte[] {0x01, 0x0a}); + CollectionAssert.AreEqual(contacts[11].Id, new byte[] {0x01, 0x09}); + CollectionAssert.AreEqual(contacts[12].Id, new byte[] {0x01, 0x08}); + CollectionAssert.AreEqual(contacts[13].Id, new byte[] {0x01, 0x0f}); + CollectionAssert.AreEqual(contacts[14].Id, new byte[] {0x01, 0x0e}); + CollectionAssert.AreEqual(contacts[15].Id, new byte[] {0x01, 0x0d}); + CollectionAssert.AreEqual(contacts[16].Id, new byte[] {0x01, 0x0c}); + CollectionAssert.AreEqual(contacts[17].Id, new byte[] {0x01, 0x13}); + CollectionAssert.AreEqual(contacts[18].Id, new byte[] {0x01, 0x12}); + CollectionAssert.AreEqual(contacts[19].Id, new byte[] {0x01, 0x11}); + CollectionAssert.AreEqual(contacts[20].Id, new byte[] {0x01, 0x10}); + CollectionAssert.AreEqual(contacts[21].Id, new byte[] {0x80, 0x03}); + } + } +} diff --git a/src/Catalyst.KBucket.Tests/CollectionTest.cs b/src/Catalyst.KBucket.Tests/CollectionTest.cs new file mode 100644 index 0000000000..63fa246880 --- /dev/null +++ b/src/Catalyst.KBucket.Tests/CollectionTest.cs @@ -0,0 +1,205 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Catalyst.KBucket +{ + [TestClass] + public sealed class CollectionTest + { + [TestMethod] + public void Add() + { + KBucket bucket = new(); + Contact x = new("1"); + bucket.Add(x); + Assert.AreEqual(1, bucket.Count); + Assert.IsTrue(bucket.Contains(x)); + } + + [TestMethod] + public void AddDuplicate() + { + KBucket bucket = new(); + Contact x = new("1"); + bucket.Add(x); + bucket.Add(x); + Assert.AreEqual(1, bucket.Count); + Assert.IsTrue(bucket.Contains(x)); + } + + [TestMethod] + public void AddBadContact() + { + KBucket bucket = new(); + ExceptionAssert.Throws(() => bucket.Add(null)); + ExceptionAssert.Throws(() => bucket.Add(new Contact("a") {Id = null})); + ExceptionAssert.Throws(() => bucket.Add(new Contact("a") {Id = new byte[0]})); + } + + [TestMethod] + public void TryGet() + { + KBucket bucket = new(); + Contact alpha = new("alpha"); + Contact beta = new("beta"); + bucket.Add(alpha); + + var q = bucket.TryGet(alpha.Id, out var found); + Assert.IsTrue(q); + Assert.AreSame(alpha, found); + + q = bucket.TryGet(beta.Id, out var notfound); + Assert.IsFalse(q); + Assert.IsNull(notfound); + } + + [TestMethod] + public void Count() + { + KBucket bucket = new(); + Assert.AreEqual(0, bucket.Count); + + bucket.Add(new Contact("a")); + bucket.Add(new Contact("a")); + bucket.Add(new Contact("a")); + bucket.Add(new Contact("b")); + bucket.Add(new Contact("b")); + bucket.Add(new Contact("c")); + bucket.Add(new Contact("d")); + bucket.Add(new Contact("c")); + bucket.Add(new Contact("d")); + bucket.Add(new Contact("e")); + bucket.Add(new Contact("f")); + bucket.Add(new Contact("a")); + Assert.AreEqual(6, bucket.Count); + } + + [TestMethod] + public void Clear() + { + KBucket bucket = new(); + Assert.AreEqual(0, bucket.Count); + + bucket.Add(new Contact("a")); + bucket.Add(new Contact("b")); + bucket.Add(new Contact("c")); + Assert.AreEqual(3, bucket.Count); + + bucket.Clear(); + Assert.AreEqual(0, bucket.Count); + } + + [TestMethod] + public void Remove() + { + KBucket bucket = new(); + Assert.AreEqual(0, bucket.Count); + + bucket.Add(new Contact("a")); + bucket.Add(new Contact("b")); + bucket.Add(new Contact("c")); + Assert.AreEqual(3, bucket.Count); + + bucket.Remove(new Contact("b")); + Assert.AreEqual(2, bucket.Count); + + Assert.IsTrue(bucket.Contains(new Contact("a"))); + Assert.IsFalse(bucket.Contains(new Contact("b"))); + Assert.IsTrue(bucket.Contains(new Contact("c"))); + } + + [TestMethod] + public void CopyTo() + { + KBucket bucket = new(); + Assert.AreEqual(0, bucket.Count); + + bucket.Add(new Contact("a")); + bucket.Add(new Contact("b")); + bucket.Add(new Contact("c")); + Assert.AreEqual(3, bucket.Count); + + var array = new Contact[bucket.Count + 2]; + bucket.CopyTo(array, 1); + Assert.IsNull(array[0]); + Assert.IsNotNull(array[1]); + Assert.IsNotNull(array[2]); + Assert.IsNotNull(array[3]); + Assert.IsNull(array[4]); + } + + [TestMethod] + public void Enumerate() + { + KBucket bucket = new(); + var nContacts = bucket.ContactsPerBucket + 1; + for (var i = 0; i < nContacts; ++i) + { + bucket.Add(new Contact(i)); + } + + Assert.AreEqual(nContacts, bucket.Count); + + var n = 0; + + foreach (var _ in bucket) + { + ++n; + } + + Assert.AreEqual(nContacts, n); + } + + [TestMethod] + public void CanBeModified() { Assert.IsFalse(new KBucket().IsReadOnly); } + + [TestMethod] + public async Task ThreadSafe() + { + KBucket bucket = new(); + const int nContacts = 1000; + const int nTasks = 100; + var tasks = new Task[nTasks]; + + for (var i = 0; i < nTasks; ++i) + { + var start = i; + tasks[i] = Task.Run(() => AddTask(bucket, start, nContacts)); + } + + await Task.WhenAll(tasks); + } + + public void AddTask(KBucket bucket, int start, int count) + { + for (var i = 0; i < count; ++i) + { + bucket.Add(new Contact(start * count + i)); + } + } + } +} diff --git a/src/Catalyst.KBucket.Tests/Contact.cs b/src/Catalyst.KBucket.Tests/Contact.cs new file mode 100644 index 0000000000..0db00f8878 --- /dev/null +++ b/src/Catalyst.KBucket.Tests/Contact.cs @@ -0,0 +1,50 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Globalization; +using System.Security.Cryptography; +using System.Text; + +namespace Catalyst.KBucket +{ + public class Contact : IContact + { + public Contact(string id) + { + var key = Encoding.UTF8.GetBytes(id); + using (var hasher = SHA1.Create()) + { + Id = hasher.ComputeHash(key); + } + } + + public Contact(int id) + : this(id.ToString(CultureInfo.InvariantCulture)) { } + + public Contact(params byte[] bytes) { Id = bytes; } + + public byte[] Id { get; set; } + + public int Clock { get; set; } + } +} diff --git a/src/Catalyst.KBucket.Tests/DetermineNodeTest.cs b/src/Catalyst.KBucket.Tests/DetermineNodeTest.cs new file mode 100644 index 0000000000..d92f6998b5 --- /dev/null +++ b/src/Catalyst.KBucket.Tests/DetermineNodeTest.cs @@ -0,0 +1,69 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Catalyst.KBucket +{ + /// + /// From https://github.com/tristanls/k-bucket/blob/master/test/determineNode.js + /// + [TestClass] + public class DetermineNodeTest + { + private static readonly Bucket Left = new(); + private static readonly Bucket Right = new(); + private static readonly Bucket Root = new Bucket {Left = Left, Right = Right}; + + [TestMethod] + public void Tests() + { + KBucket kBucket = new(); + Bucket actual; + + actual = kBucket._DetermineNode(Root, new byte[] {0x00}, 0); + Assert.AreSame(Left, actual); + + actual = kBucket._DetermineNode(Root, new byte[] {0x40}, 0); + Assert.AreSame(Left, actual); + + actual = kBucket._DetermineNode(Root, new byte[] {0x40}, 1); + Assert.AreSame(Right, actual); + + actual = kBucket._DetermineNode(Root, new byte[] {0x40}, 2); + Assert.AreSame(Left, actual); + + actual = kBucket._DetermineNode(Root, new byte[] {0x40}, 9); + Assert.AreSame(Left, actual); + + actual = kBucket._DetermineNode(Root, new byte[] {0x41}, 7); + Assert.AreSame(Right, actual); + + actual = kBucket._DetermineNode(Root, new byte[] {0x41, 0x00}, 7); + Assert.AreSame(Right, actual); + + actual = kBucket._DetermineNode(Root, new byte[] {0x00, 0x41, 0x00}, 15); + Assert.AreSame(Right, actual); + } + } +} diff --git a/src/Catalyst.KBucket.Tests/DistanceTest.cs b/src/Catalyst.KBucket.Tests/DistanceTest.cs new file mode 100644 index 0000000000..bc4a01148a --- /dev/null +++ b/src/Catalyst.KBucket.Tests/DistanceTest.cs @@ -0,0 +1,63 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Catalyst.KBucket +{ + [TestClass] + public class DistanceTest + { + /// + /// From https://github.com/tristanls/k-bucket/blob/master/test/defaultDistance.js + /// + [TestMethod] + public void Tristanls() + { + KBucket bucket = new(); + + Assert.AreEqual((ulong) 0, bucket.Distance(new byte[] {0x00}, new byte[] {0x00})); + Assert.AreEqual((ulong) 1, bucket.Distance(new byte[] {0x00}, new byte[] {0x01})); + Assert.AreEqual((ulong) 3, bucket.Distance(new byte[] {0x02}, new byte[] {0x01})); + Assert.AreEqual((ulong) 255, bucket.Distance(new byte[] {0x00}, new byte[] {0x00, 0x00})); + Assert.AreEqual((ulong) 16640, bucket.Distance(new byte[] {0x01, 0x24}, new byte[] {0x40, 0x24})); + } + + [TestMethod] + public void ByContact() + { + KBucket bucket = new(); + Contact c0 = new((byte) 0); + Contact c1 = new((byte) 1); + Contact c2 = new((byte) 2); + Contact c00 = new((byte) 0, (byte) 0); + Contact c0124 = new((byte) 0x01, (byte) 0x24); + Contact c4024 = new((byte) 0x40, (byte) 0x24); + Assert.AreEqual((ulong) 0, bucket.Distance(c0, c0)); + Assert.AreEqual((ulong) 1, bucket.Distance(c0, c1)); + Assert.AreEqual((ulong) 3, bucket.Distance(c2, c1)); + Assert.AreEqual((ulong) 255, bucket.Distance(c0, c00)); + Assert.AreEqual((ulong) 16640, bucket.Distance(c0124, c4024)); + } + } +} diff --git a/src/Catalyst.KBucket.Tests/ExceptionAssert.cs b/src/Catalyst.KBucket.Tests/ExceptionAssert.cs new file mode 100644 index 0000000000..6421c5df8d --- /dev/null +++ b/src/Catalyst.KBucket.Tests/ExceptionAssert.cs @@ -0,0 +1,71 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Catalyst.KBucket +{ + /// + /// Asserting an . + /// + internal static class ExceptionAssert + { + public static T Throws(Action action, string expectedMessage = null) where T : Exception + { + try + { + action(); + } + catch (AggregateException e) + { + var match = e.InnerExceptions.OfType().FirstOrDefault(); + if (match != null) + { + if (expectedMessage != null) + Assert.AreEqual(expectedMessage, match.Message, "Wrong exception message."); + return match; + } + + throw; + } + catch (T e) + { + if (expectedMessage != null) + Assert.AreEqual(expectedMessage, e.Message); + return e; + } + + Assert.Fail("Exception of type {0} should be thrown.", typeof(T)); + + // The compiler doesn't know that Assert.Fail will always throw an exception + return null; + } + + public static Exception Throws(Action action, string expectedMessage = null) + { + return Throws(action, expectedMessage); + } + } +} diff --git a/src/Catalyst.KBucket.Tests/SplitTest.cs b/src/Catalyst.KBucket.Tests/SplitTest.cs new file mode 100644 index 0000000000..cf6c9d98bc --- /dev/null +++ b/src/Catalyst.KBucket.Tests/SplitTest.cs @@ -0,0 +1,135 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Catalyst.KBucket +{ + /// + /// From https://github.com/tristanls/k-bucket/blob/master/test/split.js + /// + [TestClass] + public class SplitTest + { + [TestMethod] + public void OneContactDoesNotSplit() + { + var kBucket = new KBucket + { + new Contact("a") + }; + Assert.IsNull(kBucket.Root.Left); + Assert.IsNull(kBucket.Root.Right); + Assert.IsNotNull(kBucket.Root.Contacts); + } + + [TestMethod] + public void MaxContactsPerNodeDoesNotSplit() + { + KBucket kBucket = new(); + for (var i = 0; i < kBucket.ContactsPerBucket; ++i) kBucket.Add(new Contact(i)); + + Assert.IsNull(kBucket.Root.Left); + Assert.IsNull(kBucket.Root.Right); + Assert.IsNotNull(kBucket.Root.Contacts); + } + + [TestMethod] + public void MaxContactsPerNodePlusOneDoetSplit() + { + KBucket kBucket = new(); + for (var i = 0; i < kBucket.ContactsPerBucket + 1; ++i) kBucket.Add(new Contact(i)); + + Assert.IsNotNull(kBucket.Root.Left); + Assert.IsNotNull(kBucket.Root.Right); + Assert.IsNull(kBucket.Root.Contacts); + } + + [TestMethod] + public void SplitNodesContainsAllContacts() + { + var kBucket = new KBucket + { + LocalContactId = new byte[] {0x00} + }; + List contacts = new(); + for (var i = 0; i < kBucket.ContactsPerBucket + 1; ++i) + { + Contact contact = new((byte) i); + contacts.Add(contact); + kBucket.Add(contact); + } + + foreach (var contact in contacts) Assert.IsTrue(kBucket.Contains(contact)); + } + + [TestMethod] + public void FarAway() + { + var kBucket = new KBucket + { + LocalContactId = new byte[] {0x00} + }; + for (var i = 0; i < kBucket.ContactsPerBucket + 1; ++i) kBucket.Add(new Contact((byte) i)); + + // since localNodeId is 0x00, we expect every right node to be "far" and + // therefore marked as "dontSplit = true" + // there will be one "left" node and four "right" nodes (t.expect(5)) + Traverse(kBucket.Root, false); + } + + private void Traverse(Bucket node, bool dontSplit) + { + if (node.Contacts == null) + { + Traverse(node.Left, false); + Traverse(node.Right, true); + } + else + { + Assert.AreEqual(dontSplit, node.DontSplit); + } + } + + [TestMethod] + public void PingEvent() + { + var kBucket = new KBucket + { + LocalContactId = new byte[] {0x00} + }; + var pings = 0; + kBucket.Ping += (s, e) => + { + kBucket.Remove(e.Oldest.First()); + kBucket.Add(e.Newest); + ++pings; + }; + + for (var i = 0; i < 0x255; ++i) kBucket.Add(new Contact((byte) i)); + Assert.AreNotEqual(0, pings); + } + } +} diff --git a/src/Catalyst.KBucket.Tests/UpdateTest.cs b/src/Catalyst.KBucket.Tests/UpdateTest.cs new file mode 100644 index 0000000000..bf1032e2d5 --- /dev/null +++ b/src/Catalyst.KBucket.Tests/UpdateTest.cs @@ -0,0 +1,57 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Catalyst.KBucket +{ + /// + /// From https://github.com/tristanls/k-bucket/blob/master/test/update.js + /// + [TestClass] + public class UpdateTest + { + [TestMethod] + public void ContactDrop() + { + var kBucket = new KBucket + { + Arbiter = (a, b) => a.Clock > b.Clock ? a : b + }; + Contact a3 = new("a") {Clock = 3}; + Contact a2 = new("a") {Clock = 2}; + Contact a4 = new("a") {Clock = 4}; + + kBucket.Add(a3); + kBucket.Add(a2); + Assert.AreEqual(1, kBucket.Count); + Assert.IsTrue(kBucket.TryGet(a3.Id, out var current)); + Assert.AreSame(a3, current); + + kBucket.Add(a4); + Assert.AreEqual(1, kBucket.Count); + Assert.IsTrue(kBucket.TryGet(a4.Id, out current)); + Assert.AreSame(a4, current); + } + } +} diff --git a/src/Catalyst.KBucket/.gitattributes b/src/Catalyst.KBucket/.gitattributes new file mode 100644 index 0000000000..e1ff695e7b --- /dev/null +++ b/src/Catalyst.KBucket/.gitattributes @@ -0,0 +1,3 @@ +* text eol=lf +*.png binary +* crlf=input diff --git a/src/Catalyst.KBucket/.gitignore b/src/Catalyst.KBucket/.gitignore new file mode 100644 index 0000000000..dc4eb00676 --- /dev/null +++ b/src/Catalyst.KBucket/.gitignore @@ -0,0 +1,204 @@ +## Ignore Visual Studio temporary files, build results, and +## files generated by popular Visual Studio add-ons. + +# User-specific files +*.suo +*.user +*.userosscache +*.sln.docstates + +# User-specific files (MonoDevelop/Xamarin Studio) +*.userprefs + +# Build results +[Dd]ebug/ +[Dd]ebugPublic/ +[Rr]elease/ +[Rr]eleases/ +x64/ +x86/ +build/ +bld/ +[Bb]in/ +[Oo]bj/ + +# docfx +api/ +doc/api/ + +# Visual Studo 2015 cache/options directory +.vs/ + +# MSTest test Results +[Tt]est[Rr]esult*/ +[Bb]uild[Ll]og.* + +# NUNIT +*.VisualState.xml +TestResult.xml + +# Build Results of an ATL Project +[Dd]ebugPS/ +[Rr]eleasePS/ +dlldata.c + +*_i.c +*_p.c +*_i.h +*.ilk +*.meta +*.obj +*.pch +*.pdb +*.pgc +*.pgd +*.rsp +*.sbr +*.tlb +*.tli +*.tlh +*.tmp +*.tmp_proj +*.log +*.vspscc +*.vssscc +.builds +*.pidb +*.svclog +*.scc + +# Chutzpah Test files +_Chutzpah* + +# Visual C++ cache files +ipch/ +*.aps +*.ncb +*.opensdf +*.sdf +*.cachefile + +# Visual Studio profiler +*.psess +*.vsp +*.vspx + +# TFS 2012 Local Workspace +$tf/ + +# Guidance Automation Toolkit +*.gpState + +# ReSharper is a .NET coding add-in +_ReSharper*/ +*.[Rr]e[Ss]harper +*.DotSettings.user + +# JustCode is a .NET coding addin-in +.JustCode + +# TeamCity is a build add-in +_TeamCity* + +# DotCover is a Code Coverage Tool +*.dotCover + +# NCrunch +_NCrunch_* +.*crunch*.local.xml + +# MightyMoose +*.mm.* +AutoTest.Net/ + +# Web workbench (sass) +.sass-cache/ + +# Installshield output folder +[Ee]xpress/ + +# DocProject is a documentation generator add-in +DocProject/buildhelp/ +DocProject/Help/*.HxT +DocProject/Help/*.HxC +DocProject/Help/*.hhc +DocProject/Help/*.hhk +DocProject/Help/*.hhp +DocProject/Help/Html2 +DocProject/Help/html + +# Click-Once directory +publish/ + +# Publish Web Output +*.[Pp]ublish.xml +*.azurePubxml +# TODO: Comment the next line if you want to checkin your web deploy settings +# but database connection strings (with potential passwords) will be unencrypted +*.pubxml +*.publishproj + +# NuGet Packages +*.nupkg +# The packages folder can be ignored because of Package Restore +**/packages/* +# except build/, which is used as an MSBuild target. +!**/packages/build/ +# Uncomment if necessary however generally it will be regenerated when needed +#!**/packages/repositories.config + +# Windows Azure Build Output +csx/ +*.build.csdef + +# Windows Store app package directory +AppPackages/ + +# Others +*.[Cc]ache +ClientBin/ +[Ss]tyle[Cc]op.* +~$* +*~ +*.dbmdl +*.dbproj.schemaview +*.pfx +*.publishsettings +node_modules/ +bower_components/ + +# RIA/Silverlight projects +Generated_Code/ + +# Backup & report files from converting an old project file +# to a newer Visual Studio version. Backup files are not needed, +# because we have git ;-) +_UpgradeReport_Files/ +Backup*/ +UpgradeLog*.XML +UpgradeLog*.htm + +# SQL Server files +*.mdf +*.ldf + +# Business Intelligence projects +*.rdl.data +*.bim.layout +*.bim_*.settings + +# Microsoft Fakes +FakesAssemblies/ + +# Node.js Tools for Visual Studio +.ntvs_analysis.dat + +# Visual Studio 6 build log +*.plg + +# Visual Studio 6 workspace options file +*.opt + +# VS 2017 creates this +src/Makaretu.Dns.xml + diff --git a/src/Catalyst.KBucket/.nuget/packages.config b/src/Catalyst.KBucket/.nuget/packages.config new file mode 100644 index 0000000000..184f9df2fc --- /dev/null +++ b/src/Catalyst.KBucket/.nuget/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/Catalyst.KBucket/Bucket.cs b/src/Catalyst.KBucket/Bucket.cs new file mode 100644 index 0000000000..7a9a6725f1 --- /dev/null +++ b/src/Catalyst.KBucket/Bucket.cs @@ -0,0 +1,132 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Linq; + +namespace Catalyst.KBucket +{ + /// + /// A binary tree node in the + /// Catalyst.KBucket{T} + /// + /// . + /// + public sealed class Bucket + where T : class, IContact + { + /// + /// The contacts in the bucket. + /// + public List Contacts = new (); + + /// + /// Determines if the bucket can be split. + /// + public bool DontSplit; + + /// + /// The left hand node. + /// + public Bucket Left; + + /// + /// The right hand node. + /// + public Bucket Right; + + /// + /// Determines if the contains the item. + /// + public bool Contains(T item) { return Contacts != null && Contacts.Any(c => c.Id.SequenceEqual(item.Id)); } + + /// + /// Gets the first contact with the ID. + /// + /// + /// + /// The matching contact or null. + /// + public T Get(IEnumerable id) { return Contacts?.FirstOrDefault(c => c.Id.SequenceEqual(id)); } + + internal int IndexOf(byte[] id) + { + if (Contacts == null) + { + return -1; + } + + return Contacts.FindIndex(c => c.Id.SequenceEqual(id)); + } + + internal int DeepCount() + { + var n = 0; + if (Contacts != null) + { + n += Contacts.Count; + } + + if (Left != null) + { + n += Left.DeepCount(); + } + + if (Right != null) + { + n += Right.DeepCount(); + } + + return n; + } + + internal IEnumerable AllContacts() + { + if (Contacts != null) + { + foreach (var contact in Contacts) + { + yield return contact; + } + } + + if (Left != null) + { + foreach (var contact in Left.AllContacts()) + { + yield return contact; + } + } + + if (Right == null) + { + yield break; + } + + foreach (var contact in Right.AllContacts()) + { + yield return contact; + } + } + } +} diff --git a/src/Catalyst.KBucket/CHANGELOG.md b/src/Catalyst.KBucket/CHANGELOG.md new file mode 100644 index 0000000000..aea591fb14 --- /dev/null +++ b/src/Catalyst.KBucket/CHANGELOG.md @@ -0,0 +1,14 @@ +# Release + +See the [release history](https://github.com/richardschneider/k-bucket/releases) for all changes. + +## Policy + +The [semantic versioning](http://semver.org/) policy is followed. This means that + +- Major version number is incremented on a breaking change +- Minor version number is incremented on a feature addition +- Patch version number is incremented on backwards compatibile bug fix + +Major version number '0' is a special case; breaking changes can occur without incrementing +the major version number. diff --git a/src/Catalyst.KBucket/Catalyst.KBucket.csproj b/src/Catalyst.KBucket/Catalyst.KBucket.csproj new file mode 100644 index 0000000000..23c545873d --- /dev/null +++ b/src/Catalyst.KBucket/Catalyst.KBucket.csproj @@ -0,0 +1,31 @@ + + + + net6.0 + Catalyst.KBucket + Catalyst.KBucket + bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml + full + + + 0.42 + 0.42 + + + Makaretu.KBucket + Richard Schneider + Kademlia DHT K-bucket + Kademlia DHT K-bucket C# implementation as a binary tree. + false + https://github.com/richardschneider/k-bucket/releases + © 2018 Richard Schneider + kademlia DHT distributed k-bucket + True + https://github.com/richardschneider/k-bucket/blob/master/LICENSE + https://github.com/richardschneider/k-bucket + + + + + + diff --git a/src/Catalyst.KBucket/IContact.cs b/src/Catalyst.KBucket/IContact.cs new file mode 100644 index 0000000000..449564e298 --- /dev/null +++ b/src/Catalyst.KBucket/IContact.cs @@ -0,0 +1,39 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Catalyst.KBucket +{ + /// + /// A peer/node in the distributed system. + /// + public interface IContact + { + /// + /// Unique identifier of the contact. + /// + /// + /// Typically a hash of a unique identifier. + /// + byte[] Id { get; } + } +} diff --git a/src/Catalyst.KBucket/KBucket.cs b/src/Catalyst.KBucket/KBucket.cs new file mode 100644 index 0000000000..5e55ee66be --- /dev/null +++ b/src/Catalyst.KBucket/KBucket.cs @@ -0,0 +1,528 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using Nethermind.Dirichlet.Numerics; + +namespace Catalyst.KBucket +{ + /// + /// Implementation of a Kademlia DHT k-bucket used for storing contact (peer node) information. + /// + /// + /// A contact type that implements . + /// + /// + /// All public methods and properties are thead-safe. + /// + public class KBucket : ICollection + where T : class, IContact + { + /// + /// + /// + private readonly ReaderWriterLockSlim _rwlock = new(); + + /// + /// + /// + private byte[] _localContactId; + + /// + /// The number of contacts allowed in a bucket. + /// + /// + /// This is the 'K' in Catalyst.KBucket. Defaults to 20. + /// + public int ContactsPerBucket { get; set; } = 20; + + /// + /// The number of contacts to ping when a bucket that should not be split + /// becomes full. + /// + /// + /// Defaults to 3. + /// + /// + public int ContactsToPing { get; set; } = 3; + + /// + /// The ID of the local contact/peer. + /// + /// + /// Defaults to 20 random bytes. + /// + public byte[] LocalContactId + { + get + { + if (_localContactId != null) return _localContactId; + + _localContactId = new byte[20]; + new Random().NextBytes(_localContactId); + + return _localContactId; + } + set => _localContactId = value; + } + + /// + /// The root of the binary tree. + /// + public Bucket Root { get; private set; } = new(); + + /// + /// Raised when a bucket needs splitting but cannot be split. + /// + public event EventHandler> Ping; + + /// + /// Determines which contact is used when both have the same ID. + /// + /// + /// Defaults to . + /// + /// + /// The arguments are the incumbent and the candidate. + /// + public Func Arbiter { get; set; } = DefaultAbiter; + + /// + /// Used to determine which contact should be use when both + /// have the same ID. + /// + /// + /// The existing contact. + /// + /// + /// The new contact. + /// + /// + /// Always returns the . + /// + public static T DefaultAbiter(T incumbent, T candidate) { return incumbent; } + + /// + /// Finds the XOR distance between the two contacts. + /// + public UInt256 Distance(T a, T b) + { + Validate(a); + Validate(b); + return Distance(a.Id, b.Id); + } + + /// + /// Finds the XOR distance between the two contact IDs. + /// + public UInt256 Distance(byte[] a, byte[] b) + { + UInt256 distance = 0; + var i = 0; + var min = Math.Min(a.Length, b.Length); + var max = Math.Max(a.Length, b.Length); + for (; i < min; ++i) distance = distance * 256 + ((ulong) a[i] ^ b[i]); + + for (; i < max; ++i) distance = distance * 256 + 255; + + return distance; + } + + /// + /// Gets the closest contacts to the provided contact. + /// + /// + /// + /// An ordered sequence of contact, sorted by closeness. + /// + /// + /// "Closest" is the XOR metric of the contact. + /// + public IEnumerable Closest(T contact) + { + Validate(contact); + return Closest(contact.Id); + } + + /// + /// Gets the closest contacts to the provided contact. + /// + /// + /// The unique of a contact. + /// + /// + /// An ordered sequence of contact, sorted by closeness. + /// + /// + /// "Closest" is the XOR metric of the contact. + /// + public IEnumerable Closest(byte[] id) + { + return this.Select(c => new + { + distance = Distance(c.Id, id), contact = c + }) + .OrderBy(a => a.distance).Select(a => a.contact); + } + + /// + public int Count => Root.DeepCount(); + + /// + public bool IsReadOnly => false; + + /// + public void Add(T item) + { + Validate(item); + bool q; + PingEventArgs e; + _rwlock.EnterWriteLock(); + try + { + q = _Add(item, out e); + } + finally + { + _rwlock.ExitWriteLock(); + } + + // Could not add. Ping oldest contacts. + if (!q) Ping?.Invoke(this, e); + } + + /// + public void Clear() { Root = new Bucket(); } + + /// + public bool Contains(T item) + { + Validate(item); + + _rwlock.EnterReadLock(); + try + { + return _Get(item.Id) != null; + } + finally + { + _rwlock.ExitReadLock(); + } + } + + /// + /// Gets the contact associated with the specified ID. + /// + /// + /// The ID of an . + /// + /// + /// When this method returns, contains the associated + /// with the , if the key is found; otherwise, null. + /// This parameter is passed uninitialized. + /// + /// + /// true if the is found; otherwise false. + /// + public bool TryGet(byte[] id, out T contact) + { + _rwlock.EnterReadLock(); + try + { + contact = _Get(id); + return contact != null; + } + finally + { + _rwlock.ExitReadLock(); + } + } + + /// + public void CopyTo(T[] array, int arrayIndex) + { + foreach (var contact in this) array[arrayIndex++] = contact; + } + + /// + public IEnumerator GetEnumerator() + { + _rwlock.EnterReadLock(); + try + { + foreach (var contact in Root.AllContacts()) yield return contact; + } + finally + { + _rwlock.ExitReadLock(); + } + } + + /// + public bool Remove(T item) + { + Validate(item); + + _rwlock.EnterWriteLock(); + try + { + return _Remove(item.Id); + } + finally + { + _rwlock.ExitWriteLock(); + } + } + + /// + IEnumerator IEnumerable.GetEnumerator() { return GetEnumerator(); } + + /// + /// Check that contact is correct. + /// + /// + /// The to validate. + /// + /// + /// When is null or its + /// is null or empty. + /// + private void Validate(T contact) + { + if (contact == null) + { + throw new ArgumentNullException(nameof(contact)); + } + + if (contact.Id == null || contact.Id.Length == 0) + { + throw new ArgumentNullException(nameof(contact)); + } + } + + /// + /// Add the contact. + /// + /// + /// + /// + /// true if the was added; otherwise, + /// false and a event should be raised. + /// + private bool _Add(T contact, out PingEventArgs ping) + { + ping = null; + + var bitIndex = 0; + var node = Root; + + while (node.Contacts == null) + + // this is not a leaf node but an inner node with 'low' and 'high' + // branches; we will check the appropriate bit of the identifier and + // delegate to the appropriate node for further processing + { + node = _DetermineNode(node, contact.Id, bitIndex++); + } + + // check if the contact already exists + var index = node.IndexOf(contact.Id); + if (0 <= index) + { + _Update(node, index, contact); + return true; + } + + if (node.Contacts.Count < ContactsPerBucket) + { + node.Contacts.Add(contact); + return true; + } + + // the bucket is full + if (node.DontSplit) + { + // we are not allowed to split the bucket + // we need to ping the first this.numberOfNodesToPing + // in order to determine if they are alive + // only if one of the pinged nodes does not respond, can the new contact + // be added (this prevents DoS flodding with new invalid contacts) + + ping = new PingEventArgs + { + Oldest = node.Contacts.Take(ContactsToPing).ToArray(), + Newest = contact + }; + return false; + } + + _Split(node, bitIndex); + return _Add(contact, out ping); + } + + /// + /// Splits the node, redistributes contacts to the new nodes, and marks the + /// node that was split as an inner node of the binary tree of nodes by + /// setting this.root.contacts = null + /// + private void _Split(Bucket node, int bitIndex) + { + node.Left = new Bucket(); + node.Right = new Bucket(); + + // redistribute existing contacts amongst the two newly created nodes + foreach (var contact in node.Contacts) + { + _DetermineNode(node, contact.Id, bitIndex) + .Contacts.Add(contact); + } + + node.Contacts = null; // mark as inner tree node + + // don't split the "far away" node + // we check where the local node would end up and mark the other one as + // "dontSplit" (i.e. "far away") + var detNode = _DetermineNode(node, LocalContactId, bitIndex); + var otherNode = node.Left == detNode ? node.Right : node.Left; + otherNode.DontSplit = true; + } + + /// + /// Updates the contact selected by the arbiter. + /// + /// + /// If the selection is our old contact and the candidate is some new contact + /// then the new contact is abandoned (not added). + /// + /// If the selection is our old contact and the candidate is our old contact + /// then we are refreshing the contact and it is marked as most recently + /// contacted(by being moved to the right/end of the bucket array). + /// + /// If the selection is our new contact, the old contact is removed and the new + /// contact is marked as most recently contacted. + /// + private void _Update(Bucket node, int index, T contact) + { + var incumbent = node.Contacts[index]; + var selection = Arbiter(incumbent, contact); + + // if the selection is our old contact and the candidate is some new + // contact, then there is nothing to do + if (selection == incumbent && incumbent != contact) + { + return; + } + + node.Contacts.RemoveAt(index); + node.Contacts.Add(selection); + } + + /// + /// Determines whether the id at the bitIndex is 0 or 1. + /// + /// + /// Left leaf if `id` at `bitIndex` is 0, right leaf otherwise + /// + /// + /// This is an internal method. It should not be directly called. + /// It is only public for unit testing. + /// + public Bucket _DetermineNode(Bucket node, byte[] id, int bitIndex) + { + // id's that are too short are put in low bucket (1 byte = 8 bits) + // (bitIndex >> 3) finds how many bytes the bitIndex describes + // bitIndex % 8 checks if we have extra bits beyond byte multiples + // if number of bytes is <= no. of bytes described by bitIndex and there + // are extra bits to consider, this means id has less bits than what + // bitIndex describes, id therefore is too short, and will be put in low + // bucket + var bytesDescribedByBitIndex = bitIndex >> 3; + var bitIndexWithinByte = bitIndex % 8; + if (id.Length <= bytesDescribedByBitIndex && bitIndexWithinByte != 0) + { + return node.Left; + } + + // byteUnderConsideration is an integer from 0 to 255 represented by 8 bits + // where 255 is 11111111 and 0 is 00000000 + // in order to find out whether the bit at bitIndexWithinByte is set + // we construct (1 << (7 - bitIndexWithinByte)) which will consist + // of all bits being 0, with only one bit set to 1 + // for example, if bitIndexWithinByte is 3, we will construct 00010000 by + // (1 << (7 - 3)) -> (1 << 4) -> 16 + var byteUnderConsideration = id[bytesDescribedByBitIndex]; + return 0 != (byteUnderConsideration & (1 << (7 - bitIndexWithinByte))) ? node.Right : node.Left; + } + + /// + /// Get a contact by its exact ID. + /// + /// + /// The ID of a . + /// + /// + /// null or the found contact. + /// + private T _Get(byte[] id) + { + /* + * If this is a leaf, loop through the bucket contents and return the correct + * contact if we have it or null if not. If this is an inner node, determine + * which branch of the tree to traverse and repeat. + */ + var bitIndex = 0; + var node = Root; + while (node.Contacts == null) + { + node = _DetermineNode(node, id, bitIndex++); + } + + return node.Get(id); + } + + private bool _Remove(byte[] id) + { + var bitIndex = 0; + + var node = Root; + while (node.Contacts == null) + { + node = _DetermineNode(node, id, bitIndex++); + } + + // index of uses contact id for matching + var index = node.IndexOf(id); + if (0 > index) + { + return false; + } + + node.Contacts.RemoveAt(index); + return true; + } + } +} diff --git a/src/Catalyst.KBucket/LICENSE b/src/Catalyst.KBucket/LICENSE new file mode 100644 index 0000000000..61d5419047 --- /dev/null +++ b/src/Catalyst.KBucket/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2018 Richard Schneider + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/src/Catalyst.KBucket/PingEventArgs.cs b/src/Catalyst.KBucket/PingEventArgs.cs new file mode 100644 index 0000000000..01d71e3e40 --- /dev/null +++ b/src/Catalyst.KBucket/PingEventArgs.cs @@ -0,0 +1,46 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Net.NetworkInformation; + +namespace Catalyst.KBucket +{ + /// + /// The contacts that should be checked. + /// + /// + public class PingEventArgs : EventArgs where T : IContact + { + /// + /// The contacts that should be checked. + /// + public IEnumerable Oldest; + + /// + /// A new contact that wants to be added. + /// + public T Newest; + } +} diff --git a/src/Catalyst.KBucket/README.md b/src/Catalyst.KBucket/README.md new file mode 100644 index 0000000000..ced242d267 --- /dev/null +++ b/src/Catalyst.KBucket/README.md @@ -0,0 +1,27 @@ +# KBucket +[![build status](https://ci.appveyor.com/api/projects/status/github/richardschneider/k-bucket?branch=master&svg=true)](https://ci.appveyor.com/project/richardschneider/k-bucket) +[![CircleCI](https://circleci.com/gh/richardschneider/k-bucket.svg?style=svg)](https://circleci.com/gh/richardschneider/k-bucket) +[![Coverage Status](https://coveralls.io/repos/richardschneider/k-bucket/badge.svg?branch=master&service=github)](https://coveralls.io/github/richardschneider/k-bucket?branch=master) +[![Version](https://img.shields.io/nuget/v/Makaretu.KBucket.svg)](https://www.nuget.org/packages/Makaretu.KBucket) +[![docs](https://cdn.rawgit.com/richardschneider/k-bucket/master/doc/images/docs-latest-green.svg)](https://richardschneider.github.io/k-bucket/articles/intro.html) + +A [Distributed Hash Table](http://en.wikipedia.org/wiki/Distributed_hash_table) (DHT) is a +decentralised distributed system that provides a lookup table similar to a centralised hash table. +**k-bucket** is an implementation of a storage mechanism for keys within a DHT. It +manages `IContact` objects which represent nodes in the distributed system. +`contact` objects have an ID, which is typically a SHA-1 hash. + +## Getting started + +Published releases are available on [NuGet](https://www.nuget.org/packages/Makaretu.KBucket). To install, +run the following command in the [Package Manager Console](https://docs.nuget.org/docs/start-here/using-the-package-manager-console). + + PM> Install-Package Makaretu.KBucket + +## Usage + +todo + +## Credits + +This is largely based on the javascript library by [tristanls](https://github.com/tristanls/k-bucket). diff --git a/src/Catalyst.Module.ConvanSmartContract.Tests/Catalyst.Module.ConvanSmartContract.Tests.csproj b/src/Catalyst.Module.ConvanSmartContract.Tests/Catalyst.Module.ConvanSmartContract.Tests.csproj new file mode 100644 index 0000000000..a8becf8cc0 --- /dev/null +++ b/src/Catalyst.Module.ConvanSmartContract.Tests/Catalyst.Module.ConvanSmartContract.Tests.csproj @@ -0,0 +1,26 @@ + + + + net6.0 + + false + + + + + + + + + + + + + + + + + + + + diff --git a/src/Catalyst.Module.ConvanSmartContract.Tests/UnitTest1.cs b/src/Catalyst.Module.ConvanSmartContract.Tests/UnitTest1.cs new file mode 100644 index 0000000000..68358e6208 --- /dev/null +++ b/src/Catalyst.Module.ConvanSmartContract.Tests/UnitTest1.cs @@ -0,0 +1,46 @@ +#region LICENSE + +/** +* Copyright (c) 2019 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Module.ConvanSmartContract; +using FluentAssertions; +using FluentAssertions.Json; +using Nethermind.Serialization.Json.Abi; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace Nethermind.Abi.Test.Json +{ + public class AbiDefinitionParserTests + { + [TestCase(typeof(ValidatorSet))] + public void Can_load_contract(Type contractType) + { + var parser = new AbiDefinitionParser(); + var json = parser.LoadContract(contractType); + var contract = parser.Parse(json); + var serialized = parser.Serialize(contract); + JToken.Parse(serialized).Should().ContainSubtree(json); + } + } +} diff --git a/src/Catalyst.Module.ConvanSmartContract/Catalyst.Module.ConvanSmartContract.csproj b/src/Catalyst.Module.ConvanSmartContract/Catalyst.Module.ConvanSmartContract.csproj new file mode 100644 index 0000000000..14d4718381 --- /dev/null +++ b/src/Catalyst.Module.ConvanSmartContract/Catalyst.Module.ConvanSmartContract.csproj @@ -0,0 +1,25 @@ + + + + Library + net6.0 + + + + + + + + + + + + + + + + + + + + diff --git a/src/Catalyst.Module.ConvanSmartContract/Contract/ValidatorSetContract.cs b/src/Catalyst.Module.ConvanSmartContract/Contract/ValidatorSetContract.cs new file mode 100644 index 0000000000..5bc9870248 --- /dev/null +++ b/src/Catalyst.Module.ConvanSmartContract/Contract/ValidatorSetContract.cs @@ -0,0 +1,69 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Contract; +using Nethermind.Abi; +using Nethermind.Core; +using Nethermind.Serialization.Json.Abi; + +namespace Catalyst.Module.ConvanSmartContract.Contract +{ + public class ValidatorSetContract : IValidatorSetContract + { + private readonly IAbiEncoder _abiEncoder; + private readonly ICallableContractProxy _callableContractProxy; + + internal static readonly AbiDefinition Definition = new AbiDefinitionParser().Parse(); + internal static readonly string GetValidatorsFunction = Definition.GetFunctionName(nameof(GetValidators)); + + public ValidatorSetContract(IAbiEncoder abiEncoder, ICallableContractProxy callableContractProxy) + { + _abiEncoder = abiEncoder; + _callableContractProxy = callableContractProxy; + } + + public Address[] GetValidators(Address contractAddress) + { + var data = _abiEncoder.Encode(Definition.GetFunction(GetValidatorsFunction).GetCallInfo()); + + var returnData = _callableContractProxy.Call(contractAddress, data); + + return DecodeAddresses(returnData); + } + + private Address[] DecodeAddresses(byte[] data) + { + if (data.Length == 0) + { + return new Address[] { }; + } + var objects = _abiEncoder.Decode(Definition.GetFunction(GetValidatorsFunction).GetReturnInfo(), data); + return GetAddresses(objects); + } + + private static Address[] GetAddresses(object[] objects) + { + return (Address[])objects[0]; + } + } +} diff --git a/src/Catalyst.Module.ConvanSmartContract/ValidatorSet.cs b/src/Catalyst.Module.ConvanSmartContract/ValidatorSet.cs new file mode 100644 index 0000000000..8cb47210f2 --- /dev/null +++ b/src/Catalyst.Module.ConvanSmartContract/ValidatorSet.cs @@ -0,0 +1,49 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Contract; +using Nethermind.Core; + +namespace Catalyst.Module.ConvanSmartContract +{ + public class ValidatorSet + { + private readonly IValidatorSetContract _validatorSetContract; + private readonly Address _contractAddress; + + public ValidatorSet(IValidatorSetContract validatorSetContract, Address contractAddress) + { + _validatorSetContract = validatorSetContract; + _contractAddress = contractAddress; + } + + /// + /// Get current validator set (last enacted or initial if no changes ever made) + /// function getValidators() constant returns (address[] _validators); + /// + public Address[] GetValidators() + { + return _validatorSetContract.GetValidators(_contractAddress); + } + } +} diff --git a/src/Catalyst.Module.ConvanSmartContract/ValidatorSet.json b/src/Catalyst.Module.ConvanSmartContract/ValidatorSet.json new file mode 100644 index 0000000000..0022294b9a --- /dev/null +++ b/src/Catalyst.Module.ConvanSmartContract/ValidatorSet.json @@ -0,0 +1,82 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "name": "_parentHash", + "type": "bytes32" + }, + { + "indexed": false, + "name": "_newSet", + "type": "address[]" + } + ], + "name": "InitiateChange", + "type": "event" + }, + { + "constant": false, + "inputs": [], + "name": "finalizeChange", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "validator", + "type": "address" + }, + { + "name": "blockNumber", + "type": "uint256" + } + ], + "name": "reportBenign", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": false, + "inputs": [ + { + "name": "validator", + "type": "address" + }, + { + "name": "blockNumber", + "type": "uint256" + }, + { + "name": "proof", + "type": "bytes" + } + ], + "name": "reportMalicious", + "outputs": [], + "payable": false, + "stateMutability": "nonpayable", + "type": "function" + }, + { + "constant": true, + "inputs": [], + "name": "getValidators", + "outputs": [ + { + "name": "", + "type": "address[]" + } + ], + "payable": false, + "stateMutability": "view", + "type": "function" + } +] diff --git a/src/Catalyst.Modules.Network.Dotnetty.Tests/Catalyst.Modules.Network.Dotnetty.Tests.csproj b/src/Catalyst.Modules.Network.Dotnetty.Tests/Catalyst.Modules.Network.Dotnetty.Tests.csproj new file mode 100644 index 0000000000..b338453934 --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty.Tests/Catalyst.Modules.Network.Dotnetty.Tests.csproj @@ -0,0 +1,29 @@ + + + + net6.0 + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + + + + + diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/DownloadFileTransferFactoryTests.cs b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/DownloadFileTransferFactoryTests.cs similarity index 92% rename from src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/DownloadFileTransferFactoryTests.cs rename to src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/DownloadFileTransferFactoryTests.cs index 93588fffbe..6fcd2890ee 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/DownloadFileTransferFactoryTests.cs +++ b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/DownloadFileTransferFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,20 +24,20 @@ using System; using System.Threading; using System.Threading.Tasks; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.FileTransfer; using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Protocol.Rpc.Node; using Catalyst.TestUtils; using FluentAssertions; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.FileTransfer; -namespace Catalyst.Core.Lib.Tests.UnitTests.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.Tests.UnitTests.FileTransfer { public sealed class DownloadFileTransferFactoryTests : IDisposable { @@ -49,10 +49,10 @@ public DownloadFileTransferFactoryTests() _downloadFileTransferFactory = new DownloadFileTransferFactory(Substitute.For()); } - [Fact] + [Test] public async Task Can_Cancel_Download() { - using (var cancellationTokenSource = new CancellationTokenSource()) + using (CancellationTokenSource cancellationTokenSource = new()) { var token = cancellationTokenSource.Token; SetupDownload(token); @@ -76,7 +76,7 @@ public async Task Can_Cancel_Download() } } - [Fact] + [Test] public void Can_Download_Chunk() { SetupDownload(CancellationToken.None); @@ -93,7 +93,7 @@ public void Can_Download_Chunk() _downloadFileInformation.Received(1).UpdateChunkIndicator(0, true); } - [Fact] + [Test] public void Can_Send_Error_On_Chunk_Bytes_Overflow() { SetupDownload(CancellationToken.None); @@ -106,7 +106,7 @@ public void Can_Send_Error_On_Chunk_Bytes_Overflow() }).Should().Be(FileTransferResponseCodeTypes.Error); } - [Fact] + [Test] public void Can_Send_Error_Response_When_Downloading_Bad_Chunk() { _downloadFileTransferFactory.DownloadChunk(new TransferFileBytesRequest()).Should() diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/DownloadFileTransferInformationTests.cs b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/DownloadFileTransferInformationTests.cs similarity index 76% rename from src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/DownloadFileTransferInformationTests.cs rename to src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/DownloadFileTransferInformationTests.cs index fb2635b4ff..79007b62b0 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/DownloadFileTransferInformationTests.cs +++ b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/DownloadFileTransferInformationTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,31 +24,31 @@ using System; using System.IO; using System.Linq; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Core.Lib.Config; -using Catalyst.Core.Lib.FileTransfer; using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.FileTransfer; using FluentAssertions; -using Xunit; +using NUnit.Framework; -namespace Catalyst.Core.Lib.Tests.UnitTests.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.Tests.UnitTests.FileTransfer { public sealed class DownloadFileTransferInformationTests : IDisposable { - private readonly IDownloadFileInformation _downloadFileInformation; + private IDownloadFileInformation _downloadFileInformation; - public DownloadFileTransferInformationTests() + [SetUp] + public void Init() { - _downloadFileInformation = - new DownloadFileTransferInformation(null, - null, - null, - CorrelationId.GenerateCorrelationId(), - "", - 10); + _downloadFileInformation = new DownloadFileTransferInformation(null, + null, + null, + CorrelationId.GenerateCorrelationId(), + "", + 10); } - [Fact] + [Test] public void Can_Write_To_File_When_Received_Chunk() { var bytes = Enumerable.Range(1, 10).Select(e => (byte) e).ToArray(); @@ -58,9 +58,8 @@ public void Can_Write_To_File_When_Received_Chunk() writtenBytes.SequenceEqual(bytes).Should().BeTrue(); } - [Theory] - [InlineData(2)] - [InlineData(3)] + [TestCase(2u)] + [TestCase(3u)] public void Can_Set_File_Length(uint chunkAmount) { var byteLenForChunkAmount = (ulong) (Constants.FileTransferChunkSize * chunkAmount); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/UploadFileTransferFactoryTests.cs b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/UploadFileTransferFactoryTests.cs similarity index 83% rename from src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/UploadFileTransferFactoryTests.cs rename to src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/UploadFileTransferFactoryTests.cs index 8a822d6ad8..d0d4512a55 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/UploadFileTransferFactoryTests.cs +++ b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/UploadFileTransferFactoryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,27 +23,27 @@ using System.Threading; using System.Threading.Tasks; -using Catalyst.Abstractions.FileTransfer; -using Catalyst.Core.Lib.FileTransfer; using Catalyst.Core.Lib.IO.Messaging.Correlation; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.FileTransfer; -namespace Catalyst.Core.Lib.Tests.UnitTests.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.Tests.UnitTests.FileTransfer { public class UploadFileTransferFactoryTests { - private readonly IUploadFileTransferFactory _uploadFileTransferFactory; + private IUploadFileTransferFactory _uploadFileTransferFactory; - public UploadFileTransferFactoryTests() + [SetUp] + public void Init() { _uploadFileTransferFactory = new UploadFileTransferFactory(Substitute.For()); } - [Theory] - [InlineData(2)] - [InlineData(3)] + [TestCase(2u)] + [TestCase(3u)] public async Task Can_Upload_File(uint numberOfChunks) { var uploadFileInformation = Substitute.For(); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/UploadFileTransferInformationTests.cs b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/UploadFileTransferInformationTests.cs similarity index 77% rename from src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/UploadFileTransferInformationTests.cs rename to src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/UploadFileTransferInformationTests.cs index 5bbdd53572..51b7e754c6 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/FileTransfer/UploadFileTransferInformationTests.cs +++ b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/FileTransfer/UploadFileTransferInformationTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,32 +25,31 @@ using System.Linq; using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.FileTransfer; using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Modules.Network.Dotnetty.FileTransfer; using Catalyst.Protocol.Rpc.Node; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; +using NUnit.Framework; -namespace Catalyst.Core.Lib.Tests.UnitTests.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.Tests.UnitTests.FileTransfer { public sealed class UploadFileTransferInformationTests { - [Theory] - [InlineData(2)] - [InlineData(3)] + [TestCase(2u)] + [TestCase(3u)] public void Can_Get_Correct_Chunk_Transfer_File_Bytes_Request_Message(uint chunks) { int byteLengthForChunks = (int) (Constants.FileTransferChunkSize * chunks); byte[] expectedBytes = Enumerable.Range(0, byteLengthForChunks).Select(i => (byte) i).ToArray(); - using (MemoryStream ms = new MemoryStream(expectedBytes)) + using (MemoryStream ms = new(expectedBytes)) { - var uploadFileInformation = new UploadFileTransferInformation( + UploadFileTransferInformation uploadFileInformation = new( ms, - PeerIdHelper.GetPeerId("test1"), - PeerIdHelper.GetPeerId("test2"), + MultiAddressHelper.GetAddress("test1"), + MultiAddressHelper.GetAddress("test2"), null, CorrelationId.GenerateCorrelationId()); @@ -67,15 +66,15 @@ public void Can_Get_Correct_Chunk_Transfer_File_Bytes_Request_Message(uint chunk } } - [Fact] + [Test] public void Should_Not_Be_Able_To_Retry_After_Max_Retry() { - using (var memoryStream = new MemoryStream()) + using (MemoryStream memoryStream = new()) { - var uploadFileInformation = new UploadFileTransferInformation( + UploadFileTransferInformation uploadFileInformation = new( memoryStream, - PeerIdHelper.GetPeerId("test1"), - PeerIdHelper.GetPeerId("test2"), + MultiAddressHelper.GetAddress("test1"), + MultiAddressHelper.GetAddress("test2"), null, CorrelationId.GenerateCorrelationId()); uploadFileInformation.RetryCount += Constants.FileTransferMaxChunkRetryCount + 1; diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Exceptions/ResponseHandlerDoesNotExistExceptionUnitTests.cs b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/IO/Exceptions/ResponseHandlerDoesNotExistExceptionUnitTests.cs similarity index 88% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Exceptions/ResponseHandlerDoesNotExistExceptionUnitTests.cs rename to src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/IO/Exceptions/ResponseHandlerDoesNotExistExceptionUnitTests.cs index 2fcbb36bdb..bee5eb0e17 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Exceptions/ResponseHandlerDoesNotExistExceptionUnitTests.cs +++ b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/IO/Exceptions/ResponseHandlerDoesNotExistExceptionUnitTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,13 +25,13 @@ using System.Runtime.Serialization.Formatters.Binary; using Catalyst.Core.Lib.Rpc.IO.Exceptions; using FluentAssertions; -using Xunit; +using NUnit.Framework; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Exceptions +namespace Catalyst.Modules.Network.Dotnetty.Tests.UnitTests.Rpc.IO.Exceptions { public sealed class ResponseHandlerDoesNotExistExceptionUnitTests { - [Fact] + [Test] public void ResponseHandlerDoesNotExistException_Should_Be_Serializable() { var exception = @@ -40,7 +40,7 @@ public void ResponseHandlerDoesNotExistException_Should_Be_Serializable() var exceptionToString = exception.ToString(); - var binaryFormatter = new BinaryFormatter(); + BinaryFormatter binaryFormatter = new(); using (var memoryStream = new MemoryStream()) { binaryFormatter.Serialize(memoryStream, exception); diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerCacheTests.cs b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerCacheTests.cs similarity index 83% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerCacheTests.cs rename to src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerCacheTests.cs index e80adf2c80..7ab79bac6d 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerCacheTests.cs +++ b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerCacheTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -38,52 +38,53 @@ using Microsoft.Reactive.Testing; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Messaging.Correlation +namespace Catalyst.Modules.Network.Dotnetty.Tests.UnitTests.Rpc.IO.Messaging.Correlation { public sealed class RpcMessageCorrelationManagerCacheTests : IDisposable { - public RpcMessageCorrelationManagerCacheTests() + [SetUp] + public void Init() { _testScheduler = new TestScheduler(); - var memoryCacheOptions = new MemoryCacheOptions(); - var memoryCache = new MemoryCache(memoryCacheOptions); + MemoryCacheOptions memoryCacheOptions = new(); + MemoryCache memoryCache = new(memoryCacheOptions); var logger = Substitute.For(); var changeTokenProvider = Substitute.For(); _cancellationTokenSource = new CancellationTokenSource(); - var expirationToken = new CancellationChangeToken(_cancellationTokenSource.Token); + CancellationChangeToken expirationToken = new(_cancellationTokenSource.Token); changeTokenProvider.GetChangeToken().Returns(expirationToken); _rpcMessageCorrelationManager = new RpcMessageCorrelationManager(memoryCache, logger, changeTokenProvider, _testScheduler); } - private readonly CancellationTokenSource _cancellationTokenSource; + private CancellationTokenSource _cancellationTokenSource; - private readonly TestScheduler _testScheduler; + private TestScheduler _testScheduler; - private readonly RpcMessageCorrelationManager _rpcMessageCorrelationManager; + private RpcMessageCorrelationManager _rpcMessageCorrelationManager; - [Fact] + [Test] public void Dispose_Should_Dispose_RpcMessageCorrelationManager() { _rpcMessageCorrelationManager.Dispose(); } - [Fact] + [Test] public async Task Message_Eviction_Should_Cause_Eviction_Event() { var peerIds = new[] { - PeerIdHelper.GetPeerId("peer1"), - PeerIdHelper.GetPeerId("peer2"), - PeerIdHelper.GetPeerId("peer3") + MultiAddressHelper.GetAddress("peer1"), + MultiAddressHelper.GetAddress("peer2"), + MultiAddressHelper.GetAddress("peer3") }; var pendingRequests = peerIds.Select(peerId => new CorrelatableMessage { - Content = new VersionRequest().ToProtocolMessage(PeerIdHelper.GetPeerId("sender"), + Content = new VersionRequest().ToProtocolMessage(MultiAddressHelper.GetAddress("sender"), CorrelationId.GenerateCorrelationId()), Recipient = peerId, SentAt = DateTimeOffset.MinValue.Add(TimeSpan.Zero) @@ -91,7 +92,7 @@ public async Task Message_Eviction_Should_Cause_Eviction_Event() var pendingResponses = pendingRequests.Select(peerId => new CorrelatableMessage { - Content = new VersionResponse().ToProtocolMessage(PeerIdHelper.GetPeerId("sender"), + Content = new VersionResponse().ToProtocolMessage(MultiAddressHelper.GetAddress("sender"), peerId.Content.CorrelationId.ToCorrelationId()), Recipient = peerId.Recipient, SentAt = DateTimeOffset.MinValue.Add(TimeSpan.Zero) diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerTests.cs b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerTests.cs similarity index 89% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerTests.cs rename to src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerTests.cs index 84787058cd..cf3790f4c1 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerTests.cs +++ b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/IO/Messaging/Correlation/RpcMessageCorrelationManagerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,19 +25,19 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.Rpc.IO.Messaging.Correlation; using Catalyst.Core.Lib.Rpc.IO.Messaging.Correlation; -using Catalyst.Core.Lib.Tests.UnitTests.IO.Messaging.Correlation; using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; using Microsoft.Reactive.Testing; using NSubstitute; -using Xunit; +using NUnit.Framework; +using Catalyst.Core.Lib.Tests.UnitTests.IO.Messaging.Correlation; -namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc.IO.Messaging.Correlation +namespace Catalyst.Modules.Network.Dotnetty.Tests.UnitTests.Rpc.IO.Messaging.Correlation { public sealed class RpcMessageCorrelationManagerTests : MessageCorrelationManagerTests { - private readonly TestScheduler _testScheduler = new TestScheduler(); + private readonly TestScheduler _testScheduler = new(); public RpcMessageCorrelationManagerTests() { @@ -50,13 +50,13 @@ public RpcMessageCorrelationManagerTests() PrepareCacheWithPendingRequests(); } - [Fact] + [Test] public void TryMatchResponseAsync_Should_Match_Existing_Records_With_Matching_Correlation_Id() { TryMatchResponseAsync_Should_Match_Existing_Records_With_Matching_Correlation_Id(); } - [Fact] + [Test] public void TryMatchResponseAsync_Should_Not_Match_Existing_Records_With_Non_Matching_Correlation_Id() { TryMatchResponseAsync_Should_Not_Match_Existing_Records_With_Non_Matching_Correlation_Id(); @@ -74,7 +74,7 @@ protected override void CheckCacheEntriesCallback() observer.Received(1).OnNext(Arg.Is>(p => p.EvictedContent.CorrelationId == firstCorrelationId - && p.PeerId.Equals(PendingRequests[0].Content.PeerId))); + && p.Address.Equals(PendingRequests[0].Content.Address))); } } } diff --git a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/RpcResponseObserverTests.cs b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/RpcResponseObserverTests.cs similarity index 81% rename from src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/RpcResponseObserverTests.cs rename to src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/RpcResponseObserverTests.cs index 84ccf1ed48..12e4595817 100644 --- a/src/Catalyst.Core.Lib.Tests/UnitTests/Rpc/RpcResponseObserverTests.cs +++ b/src/Catalyst.Modules.Network.Dotnetty.Tests/UnitTests/Rpc/RpcResponseObserverTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,50 +28,50 @@ using DotNetty.Transport.Channels; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; namespace Catalyst.Core.Lib.Tests.UnitTests.Rpc { /* We need to create a protobuf test stub response */ public sealed class RpcResponseObserverTests { - [Fact] + [Test] public void Null_Message_Throws_Exception() { var channelHandlerContext = Substitute.For(); - var senderPeerId = PeerIdHelper.GetPeerId(); + var senderAddress = MultiAddressHelper.GetAddress(); var correlationId = CorrelationId.GenerateCorrelationId(); var logger = Substitute.For(); - var responseObserver = new TestRpcResponseObserver(logger); + TestRpcResponseObserver responseObserver = new(logger); Assert.Throws(() => responseObserver .HandleResponseObserver(null, channelHandlerContext, - senderPeerId, correlationId)); + senderAddress, correlationId)); } - [Fact] + [Test] public void Null_ChannelHandlerContext_Throws_Exception() { - var senderPeerIdentifier = PeerIdHelper.GetPeerId(); + var senderAddress = MultiAddressHelper.GetAddress(); var correlationId = CorrelationId.GenerateCorrelationId(); var logger = Substitute.For(); - var responseObserver = new TestRpcResponseObserver(logger); + TestRpcResponseObserver responseObserver = new(logger); Assert.Throws(() => responseObserver .HandleResponseObserver(new VersionResponse(), null, - senderPeerIdentifier, correlationId)); + senderAddress, correlationId)); } - [Fact] + [Test] public void Null_SenderPeerIdentifierContext_Throws_Exception() { var channelHandlerContext = Substitute.For(); var correlationId = CorrelationId.GenerateCorrelationId(); var logger = Substitute.For(); - var responseObserver = new TestRpcResponseObserver(logger); + TestRpcResponseObserver responseObserver = new(logger); Assert.Throws(() => responseObserver .HandleResponseObserver(new VersionResponse(), channelHandlerContext, diff --git a/src/Catalyst.Abstractions/Cli/CommandTypes/ICommand.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Cli/CommandTypes/ICommand.cs similarity index 91% rename from src/Catalyst.Abstractions/Cli/CommandTypes/ICommand.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/Cli/CommandTypes/ICommand.cs index dfc35bd572..be80d8befd 100644 --- a/src/Catalyst.Abstractions/Cli/CommandTypes/ICommand.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Cli/CommandTypes/ICommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,7 @@ using System; -namespace Catalyst.Abstractions.Cli.CommandTypes +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.CommandTypes { public interface ICommand { diff --git a/src/Catalyst.Abstractions/Cli/CommandTypes/IMessageCommand.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Cli/CommandTypes/IMessageCommand.cs similarity index 86% rename from src/Catalyst.Abstractions/Cli/CommandTypes/IMessageCommand.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/Cli/CommandTypes/IMessageCommand.cs index 0628210254..29686d81d3 100644 --- a/src/Catalyst.Abstractions/Cli/CommandTypes/IMessageCommand.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Cli/CommandTypes/IMessageCommand.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using Catalyst.Abstractions.Rpc; +using Catalyst.Modules.Network.Dotnetty.Rpc; using Google.Protobuf; -namespace Catalyst.Abstractions.Cli.CommandTypes +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.CommandTypes { public interface IMessageCommand : ICommand where T : IMessage diff --git a/src/Catalyst.Abstractions/Cli/Commands/ICommandContext.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Cli/Commands/ICommandContext.cs similarity index 89% rename from src/Catalyst.Abstractions/Cli/Commands/ICommandContext.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/Cli/Commands/ICommandContext.cs index 4fe767240b..6201531809 100644 --- a/src/Catalyst.Abstractions/Cli/Commands/ICommandContext.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Cli/Commands/ICommandContext.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,12 +21,14 @@ #endregion +using Catalyst.Abstractions.Cli; using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.IO.Transport; using Catalyst.Abstractions.Rpc; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Rpc; +using MultiFormats; -namespace Catalyst.Abstractions.Cli.Commands +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.Commands { public interface ICommandContext { @@ -48,7 +50,7 @@ public interface ICommandContext /// Gets the peer identifier. /// The peer identifier. - PeerId PeerId { get; } + MultiAddress Address { get; } /// Gets the connected node. /// The node identifier located in configuration. diff --git a/src/Catalyst.Abstractions/FileTransfer/IDownloadFileInformation.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IDownloadFileInformation.cs similarity index 92% rename from src/Catalyst.Abstractions/FileTransfer/IDownloadFileInformation.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IDownloadFileInformation.cs index 3c59daecfb..eb111ab757 100644 --- a/src/Catalyst.Abstractions/FileTransfer/IDownloadFileInformation.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IDownloadFileInformation.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer { /// /// diff --git a/src/Catalyst.Abstractions/FileTransfer/IDownloadFileTransferFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IDownloadFileTransferFactory.cs similarity index 92% rename from src/Catalyst.Abstractions/FileTransfer/IDownloadFileTransferFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IDownloadFileTransferFactory.cs index b646763517..e34118e983 100644 --- a/src/Catalyst.Abstractions/FileTransfer/IDownloadFileTransferFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IDownloadFileTransferFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,7 +24,7 @@ using Catalyst.Abstractions.Types; using Catalyst.Protocol.Rpc.Node; -namespace Catalyst.Abstractions.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer { /// /// Handles storing of the file downloads diff --git a/src/Catalyst.Abstractions/FileTransfer/IFileTransferFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IFileTransferFactory.cs similarity index 95% rename from src/Catalyst.Abstractions/FileTransfer/IFileTransferFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IFileTransferFactory.cs index 013d36b8a2..14717d0b10 100644 --- a/src/Catalyst.Abstractions/FileTransfer/IFileTransferFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IFileTransferFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,7 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.Types; -namespace Catalyst.Abstractions.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer { /// /// The File Transfer interface diff --git a/src/Catalyst.Abstractions/FileTransfer/IFileTransferInformation.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IFileTransferInformation.cs similarity index 94% rename from src/Catalyst.Abstractions/FileTransfer/IFileTransferInformation.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IFileTransferInformation.cs index 0e8421a2f3..69e035ead2 100644 --- a/src/Catalyst.Abstractions/FileTransfer/IFileTransferInformation.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IFileTransferInformation.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,10 +24,10 @@ using System; using System.Threading; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Protocol.Peer; using DotNetty.Transport.Channels; +using MultiFormats; -namespace Catalyst.Abstractions.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer { /// /// The File transfer interface @@ -81,11 +81,11 @@ public interface IFileTransferInformation : IDisposable /// Gets or sets the recipient identifier. /// The recipient identifier. - PeerId RecipientId { get; set; } + MultiAddress Recipient { get; set; } /// Gets or sets the peer identifier. /// The peer identifier. - PeerId PeerId { get; set; } + MultiAddress Address { get; set; } /// The cancellation token CancellationToken CancellationToken { get; set; } diff --git a/src/Catalyst.Abstractions/FileTransfer/IUploadFileInformation.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IUploadFileInformation.cs similarity index 88% rename from src/Catalyst.Abstractions/FileTransfer/IUploadFileInformation.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IUploadFileInformation.cs index e636d3be94..a162c6b3e6 100644 --- a/src/Catalyst.Abstractions/FileTransfer/IUploadFileInformation.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IUploadFileInformation.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using Catalyst.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; using Catalyst.Protocol.Wire; -namespace Catalyst.Abstractions.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer { /// /// diff --git a/src/Catalyst.Abstractions/FileTransfer/IUploadFileTransferFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IUploadFileTransferFactory.cs similarity index 90% rename from src/Catalyst.Abstractions/FileTransfer/IUploadFileTransferFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IUploadFileTransferFactory.cs index b6027199c9..8d48cdba5d 100644 --- a/src/Catalyst.Abstractions/FileTransfer/IUploadFileTransferFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/FileTransfer/IUploadFileTransferFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer { /// /// Handles storing of file uploads diff --git a/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IDotnettyUdpClient.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IDotnettyUdpClient.cs new file mode 100644 index 0000000000..f4c8522ca8 --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IDotnettyUdpClient.cs @@ -0,0 +1,37 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Protocol.Wire; +using MultiFormats; +using System.Threading; +using System.Threading.Tasks; + +namespace Catalyst.Modules.Network.Dotnetty.Abstractions +{ + public interface IDotnettyUdpClient : ISocketClient + { + Task StartAsync(CancellationToken cancellationToken); + Task SendMessageAsync(ProtocolMessage message, MultiAddress recipient); + } +} diff --git a/src/Catalyst.Abstractions/IO/EventLoop/IEventLoopGroupFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IEventLoopGroupFactory.cs similarity index 92% rename from src/Catalyst.Abstractions/IO/EventLoop/IEventLoopGroupFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IEventLoopGroupFactory.cs index d69ed15b97..2cd876ab2d 100644 --- a/src/Catalyst.Abstractions/IO/EventLoop/IEventLoopGroupFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IEventLoopGroupFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,7 +24,7 @@ using System; using DotNetty.Transport.Channels; -namespace Catalyst.Abstractions.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop { public interface IEventLoopGroupFactory : IDisposable { diff --git a/src/Catalyst.Abstractions/IO/EventLoop/IEventLoopGroupFactoryConfiguration.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IEventLoopGroupFactoryConfiguration.cs similarity index 93% rename from src/Catalyst.Abstractions/IO/EventLoop/IEventLoopGroupFactoryConfiguration.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IEventLoopGroupFactoryConfiguration.cs index 32638f8eee..dfcb28915a 100644 --- a/src/Catalyst.Abstractions/IO/EventLoop/IEventLoopGroupFactoryConfiguration.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IEventLoopGroupFactoryConfiguration.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop { public interface IEventLoopGroupFactoryConfiguration { diff --git a/src/Catalyst.Abstractions/IO/EventLoop/ITcpClientEventLoopGroupFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/ITcpClientEventLoopGroupFactory.cs similarity index 88% rename from src/Catalyst.Abstractions/IO/EventLoop/ITcpClientEventLoopGroupFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/ITcpClientEventLoopGroupFactory.cs index 232cb1d12a..377c93f10e 100644 --- a/src/Catalyst.Abstractions/IO/EventLoop/ITcpClientEventLoopGroupFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/ITcpClientEventLoopGroupFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop { public interface ITcpClientEventLoopGroupFactory : IEventLoopGroupFactory { } } diff --git a/src/Catalyst.Abstractions/IO/EventLoop/ITcpServerEventLoopGroupFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/ITcpServerEventLoopGroupFactory.cs similarity index 90% rename from src/Catalyst.Abstractions/IO/EventLoop/ITcpServerEventLoopGroupFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/ITcpServerEventLoopGroupFactory.cs index 19a2163cf6..e1035e3b6f 100644 --- a/src/Catalyst.Abstractions/IO/EventLoop/ITcpServerEventLoopGroupFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/ITcpServerEventLoopGroupFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,7 @@ using DotNetty.Transport.Channels; -namespace Catalyst.Abstractions.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop { public interface ITcpServerEventLoopGroupFactory : IEventLoopGroupFactory { diff --git a/src/Catalyst.Abstractions/IO/EventLoop/IUdpClientEventLoopGroupFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IUdpClientEventLoopGroupFactory.cs similarity index 88% rename from src/Catalyst.Abstractions/IO/EventLoop/IUdpClientEventLoopGroupFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IUdpClientEventLoopGroupFactory.cs index 76e0ec70ef..c3b99de107 100644 --- a/src/Catalyst.Abstractions/IO/EventLoop/IUdpClientEventLoopGroupFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IUdpClientEventLoopGroupFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop { public interface IUdpClientEventLoopGroupFactory : IEventLoopGroupFactory { } } diff --git a/src/Catalyst.Abstractions/IO/EventLoop/IUdpServerEventLoopGroupFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IUdpServerEventLoopGroupFactory.cs similarity index 88% rename from src/Catalyst.Abstractions/IO/EventLoop/IUdpServerEventLoopGroupFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IUdpServerEventLoopGroupFactory.cs index 13164a1aa7..28a8dea1a4 100644 --- a/src/Catalyst.Abstractions/IO/EventLoop/IUdpServerEventLoopGroupFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/EventLoop/IUdpServerEventLoopGroupFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop { public interface IUdpServerEventLoopGroupFactory : IEventLoopGroupFactory { } } diff --git a/src/Catalyst.Abstractions/IO/Messaging/Dto/IMessageDto.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Messaging/Dto/IMessageDto.cs similarity index 82% rename from src/Catalyst.Abstractions/IO/Messaging/Dto/IMessageDto.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Messaging/Dto/IMessageDto.cs index 94f106f137..d24373d44f 100644 --- a/src/Catalyst.Abstractions/IO/Messaging/Dto/IMessageDto.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Messaging/Dto/IMessageDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,16 +22,16 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Protocol.Peer; using DotNetty.Transport.Channels; using Google.Protobuf; +using MultiFormats; -namespace Catalyst.Abstractions.IO.Messaging.Dto +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto { public interface IMessageDto : IAddressedEnvelope where T : IMessage { ICorrelationId CorrelationId { get; } - PeerId RecipientPeerIdentifier { get; } - PeerId SenderPeerIdentifier { get; } + MultiAddress RecipientAddress { get; } + MultiAddress SenderAddress { get; } } } diff --git a/src/Catalyst.Abstractions/IO/Messaging/Dto/IObserverDto.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Messaging/Dto/IObserverDto.cs similarity index 89% rename from src/Catalyst.Abstractions/IO/Messaging/Dto/IObserverDto.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Messaging/Dto/IObserverDto.cs index d137283029..d99ec5e49f 100644 --- a/src/Catalyst.Abstractions/IO/Messaging/Dto/IObserverDto.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Messaging/Dto/IObserverDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,7 +24,7 @@ using DotNetty.Transport.Channels; using Google.Protobuf; -namespace Catalyst.Abstractions.IO.Messaging.Dto +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto { public interface IObserverDto where T : IMessage { diff --git a/src/Catalyst.Abstractions/Rpc/IRpcServer.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Observers/IRpcRequestObserver.cs similarity index 75% rename from src/Catalyst.Abstractions/Rpc/IRpcServer.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Observers/IRpcRequestObserver.cs index a4c5c10464..652ddb4a27 100644 --- a/src/Catalyst.Abstractions/Rpc/IRpcServer.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Observers/IRpcRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,13 +22,10 @@ #endregion using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; using Catalyst.Protocol.Wire; -namespace Catalyst.Abstractions.Rpc +namespace Catalyst.Modules.Network.Dotnetty.IO.Observers { - public interface IRpcServer : IObservableMessageStreamer, ISocket - { - IRpcServerSettings Settings { get; } - } + public interface IRpcRequestObserver : IRequestMessageObserver> { } } diff --git a/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Observers/IRpcResponseObserver.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Observers/IRpcResponseObserver.cs new file mode 100644 index 0000000000..b10634d1bb --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Observers/IRpcResponseObserver.cs @@ -0,0 +1,38 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.IO.Messaging.Correlation; +using Catalyst.Abstractions.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Protocol.Wire; +using DotNetty.Transport.Channels; +using Google.Protobuf; +using MultiFormats; + +namespace Catalyst.Modules.Network.Dotnetty.IO.Observers +{ + public interface IRpcResponseObserver : IMessageObserver> + { + void HandleResponseObserver(IMessage message, IChannelHandlerContext channelHandlerContext, MultiAddress senderAddress, ICorrelationId correlationId); + } +} diff --git a/src/Catalyst.Abstractions/IO/Transport/Bootstrapping/IServerBootstrap.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Bootstrapping/IServerBootstrap.cs similarity index 88% rename from src/Catalyst.Abstractions/IO/Transport/Bootstrapping/IServerBootstrap.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Bootstrapping/IServerBootstrap.cs index fbd761a687..114e030556 100644 --- a/src/Catalyst.Abstractions/IO/Transport/Bootstrapping/IServerBootstrap.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Bootstrapping/IServerBootstrap.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,7 +25,7 @@ using System.Threading.Tasks; using DotNetty.Transport.Channels; -namespace Catalyst.Abstractions.IO.Transport.Bootstrapping +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Bootstrapping { public interface IServerBootstrap { diff --git a/src/Catalyst.Abstractions/IO/Transport/Channels/IChannelFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IChannelFactory.cs similarity index 70% rename from src/Catalyst.Abstractions/IO/Transport/Channels/IChannelFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IChannelFactory.cs index f9609d51c6..f672e876ef 100644 --- a/src/Catalyst.Abstractions/IO/Transport/Channels/IChannelFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,18 +21,17 @@ #endregion -using System.Net; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using MultiFormats; -namespace Catalyst.Abstractions.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels { - public interface IChannelFactory + public interface IChannelFactory { - Task BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + Task> BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, + MultiAddress address, X509Certificate2 certificate = null); } } diff --git a/src/Catalyst.Abstractions/IO/Transport/Channels/IObservableChannel.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IObservableChannel.cs similarity index 75% rename from src/Catalyst.Abstractions/IO/Transport/Channels/IObservableChannel.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IObservableChannel.cs index d1ad264406..2dbd72947b 100644 --- a/src/Catalyst.Abstractions/IO/Transport/Channels/IObservableChannel.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IObservableChannel.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,16 +22,14 @@ #endregion using System; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; -namespace Catalyst.Abstractions.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels { - public interface IObservableChannel + public interface IObservableChannel { IChannel Channel { get; } - IObservable> MessageStream { get; } + IObservable MessageStream { get; } } } diff --git a/src/Catalyst.Abstractions/Mempool/Models/IMempoolItem.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IP2PObservableChannel.cs similarity index 79% rename from src/Catalyst.Abstractions/Mempool/Models/IMempoolItem.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IP2PObservableChannel.cs index 40c5f6db67..b3f4535308 100644 --- a/src/Catalyst.Abstractions/Mempool/Models/IMempoolItem.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IP2PObservableChannel.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,10 +23,8 @@ using Catalyst.Protocol.Wire; -namespace Catalyst.Abstractions.Mempool.Models +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels { - public interface IMempoolItem - { - TransactionBroadcast Transaction { get; set; } - } + public interface IP2PObservableChannel : IObservableChannel { } } + diff --git a/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IRpcObservableChannel.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IRpcObservableChannel.cs new file mode 100644 index 0000000000..51f7b70cec --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IRpcObservableChannel.cs @@ -0,0 +1,31 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Protocol.Wire; + +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels +{ + public interface IRpcObservableChannel : IObservableChannel> { } +} + diff --git a/src/Catalyst.Abstractions/IO/Transport/Channels/ITcpClientChannelFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/ITcpClientChannelFactory.cs similarity index 80% rename from src/Catalyst.Abstractions/IO/Transport/Channels/ITcpClientChannelFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/ITcpClientChannelFactory.cs index c88b9b2228..c0144c37e8 100644 --- a/src/Catalyst.Abstractions/IO/Transport/Channels/ITcpClientChannelFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/ITcpClientChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels { - public interface ITcpClientChannelFactory : IChannelFactory { } + public interface ITcpClientChannelFactory : IChannelFactory { } } diff --git a/src/Catalyst.Abstractions/IO/Transport/Channels/ITcpServerChannelFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/ITcpServerChannelFactory.cs similarity index 80% rename from src/Catalyst.Abstractions/IO/Transport/Channels/ITcpServerChannelFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/ITcpServerChannelFactory.cs index e81077dadd..10dd280839 100644 --- a/src/Catalyst.Abstractions/IO/Transport/Channels/ITcpServerChannelFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/ITcpServerChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels { - public interface ITcpServerChannelFactory : IChannelFactory { } + public interface ITcpServerChannelFactory : IChannelFactory { } } diff --git a/src/Catalyst.Abstractions/IO/Transport/Channels/IUdpClientChannelFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IUdpClientChannelFactory.cs similarity index 80% rename from src/Catalyst.Abstractions/IO/Transport/Channels/IUdpClientChannelFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IUdpClientChannelFactory.cs index 4d9edf006e..49576ab3f6 100644 --- a/src/Catalyst.Abstractions/IO/Transport/Channels/IUdpClientChannelFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IUdpClientChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels { - public interface IUdpClientChannelFactory : IChannelFactory { } + public interface IUdpClientChannelFactory : IChannelFactory { } } diff --git a/src/Catalyst.Abstractions/IO/Transport/Channels/IUdpServerChannelFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IUdpServerChannelFactory.cs similarity index 80% rename from src/Catalyst.Abstractions/IO/Transport/Channels/IUdpServerChannelFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IUdpServerChannelFactory.cs index fd6ad8ad41..5733047f55 100644 --- a/src/Catalyst.Abstractions/IO/Transport/Channels/IUdpServerChannelFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/Channels/IUdpServerChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels { - public interface IUdpServerChannelFactory : IChannelFactory { } + public interface IUdpServerChannelFactory : IChannelFactory { } } diff --git a/src/Catalyst.Abstractions/IO/Transport/ISocket.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocket.cs similarity index 89% rename from src/Catalyst.Abstractions/IO/Transport/ISocket.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocket.cs index 15b7e39268..9a9b19e954 100644 --- a/src/Catalyst.Abstractions/IO/Transport/ISocket.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocket.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,7 +25,7 @@ using System.Threading.Tasks; using DotNetty.Transport.Channels; -namespace Catalyst.Abstractions.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport { public interface ISocket : IDisposable { diff --git a/src/Catalyst.Abstractions/IO/Transport/ISocketClient.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocketClient.cs similarity index 83% rename from src/Catalyst.Abstractions/IO/Transport/ISocketClient.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocketClient.cs index fac1bedb55..04d6ef4f5d 100644 --- a/src/Catalyst.Abstractions/IO/Transport/ISocketClient.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocketClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using Catalyst.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; using Google.Protobuf; -namespace Catalyst.Abstractions.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport { public interface ISocketClient : ISocket { diff --git a/src/Catalyst.Abstractions/IO/Transport/ISocketClientRegistry.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocketClientRegistry.cs similarity index 95% rename from src/Catalyst.Abstractions/IO/Transport/ISocketClientRegistry.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocketClientRegistry.cs index ab1ac5d13d..206224461a 100644 --- a/src/Catalyst.Abstractions/IO/Transport/ISocketClientRegistry.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocketClientRegistry.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,7 +26,7 @@ using System.Net; using Catalyst.Abstractions.IO.Events; -namespace Catalyst.Abstractions.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport { public interface ISocketClientRegistry where TSocketChannel : class, ISocketClient { diff --git a/src/Catalyst.Abstractions/IO/Transport/ISocketServer.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocketServer.cs similarity index 88% rename from src/Catalyst.Abstractions/IO/Transport/ISocketServer.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocketServer.cs index 27f2a3cf24..7c0eab64f8 100644 --- a/src/Catalyst.Abstractions/IO/Transport/ISocketServer.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ISocketServer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport { public interface ISocketServer : ISocket { } } diff --git a/src/Catalyst.Abstractions/IO/Transport/ITcpClient.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ITcpClient.cs similarity index 88% rename from src/Catalyst.Abstractions/IO/Transport/ITcpClient.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ITcpClient.cs index 439943ec42..771a9dbd4f 100644 --- a/src/Catalyst.Abstractions/IO/Transport/ITcpClient.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ITcpClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport { public interface ITcpClient : ISocketClient { } } diff --git a/src/Catalyst.Abstractions/IO/Transport/ITcpServer.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ITcpServer.cs similarity index 88% rename from src/Catalyst.Abstractions/IO/Transport/ITcpServer.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ITcpServer.cs index aa4f989704..c887a3e7d3 100644 --- a/src/Catalyst.Abstractions/IO/Transport/ITcpServer.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/ITcpServer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport { public interface ITcpServer : ISocketServer { } } diff --git a/src/Catalyst.Abstractions/IO/Transport/IUdpClient.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/IUdpClient.cs similarity index 88% rename from src/Catalyst.Abstractions/IO/Transport/IUdpClient.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/IUdpClient.cs index ecdef35be4..871dbf821b 100644 --- a/src/Catalyst.Abstractions/IO/Transport/IUdpClient.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/IUdpClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport { public interface IUdpClient : ISocketClient { } } diff --git a/src/Catalyst.Abstractions/IO/Transport/IUdpServer.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/IUdpServer.cs similarity index 88% rename from src/Catalyst.Abstractions/IO/Transport/IUdpServer.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/IUdpServer.cs index 66c827c820..c9758a6058 100644 --- a/src/Catalyst.Abstractions/IO/Transport/IUdpServer.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/IO/Transport/IUdpServer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,7 +21,7 @@ #endregion -namespace Catalyst.Abstractions.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport { public interface IUdpServer : ISocket { } } diff --git a/src/Catalyst.Abstractions/P2P/IO/Messaging/Broadcast/IBroadcastManager.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/P2P/IO/Broadcast/IBroadcastManager.cs similarity index 93% rename from src/Catalyst.Abstractions/P2P/IO/Messaging/Broadcast/IBroadcastManager.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/P2P/IO/Broadcast/IBroadcastManager.cs index a872bb0be9..c5442ac8ec 100644 --- a/src/Catalyst.Abstractions/P2P/IO/Messaging/Broadcast/IBroadcastManager.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/P2P/IO/Broadcast/IBroadcastManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,7 +26,7 @@ using Catalyst.Abstractions.IO.Observers; using Catalyst.Protocol.Wire; -namespace Catalyst.Abstractions.P2P.IO.Messaging.Broadcast +namespace Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast { public interface IBroadcastManager { diff --git a/src/Catalyst.Abstractions/Rpc/IO/Messaging/Dto/IRPCClientMessageDto.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IO/Messaging/Dto/IRPCClientMessageDto.cs similarity index 96% rename from src/Catalyst.Abstractions/Rpc/IO/Messaging/Dto/IRPCClientMessageDto.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IO/Messaging/Dto/IRPCClientMessageDto.cs index 97dab59487..cd8d527076 100644 --- a/src/Catalyst.Abstractions/Rpc/IO/Messaging/Dto/IRPCClientMessageDto.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IO/Messaging/Dto/IRPCClientMessageDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IO/Observers/RpcRequestObserverBase.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IO/Observers/RpcRequestObserverBase.cs new file mode 100644 index 0000000000..09a9330f6e --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IO/Observers/RpcRequestObserverBase.cs @@ -0,0 +1,85 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.IO.Messaging.Correlation; +using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.Types; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Protocol.Wire; +using Dawn; +using DotNetty.Transport.Channels; +using Google.Protobuf; +using MultiFormats; +using Serilog; + +namespace Catalyst.Modules.Network.Dotnetty.Rpc.IO.Observers +{ + public abstract class RpcRequestObserverBase : MessageObserverBase>, IRequestMessageObserver> + where TProtoReq : IMessage where TProtoRes : IMessage + { + public IPeerSettings PeerSettings { get; } + + private static Func, bool> FilterExpression = m => m?.Payload?.TypeUrl != null && m.Payload.TypeUrl == typeof(TProtoReq).ShortenedProtoFullName(); + + protected RpcRequestObserverBase(ILogger logger, IPeerSettings peerSettings) : base(logger, FilterExpression) + { + Guard.Argument(typeof(TProtoReq), nameof(TProtoReq)).Require(t => t.IsRequestType(), + t => $"{nameof(TProtoReq)} is not of type {MessageTypes.Request.Name}"); + PeerSettings = peerSettings; + logger.Verbose("{interface} instantiated", nameof(IRequestMessageObserver>)); + } + + protected abstract TProtoRes HandleRequest(TProtoReq message, IChannelHandlerContext channelHandlerContext, MultiAddress sender, ICorrelationId correlationId); + + public override void OnNext(IObserverDto messageDto) + { + Logger.Verbose("Pre Handle Message Called"); + + try + { + var correlationId = messageDto.Payload.CorrelationId.ToCorrelationId(); + var recipientAddress = messageDto.Payload.Address; + + var response = HandleRequest(messageDto.Payload.FromProtocolMessage(), + messageDto.Context, + recipientAddress, + correlationId); + + MessageDto responseDto = new( + response.ToProtocolMessage(PeerSettings.Address, correlationId), + recipientAddress); + + messageDto.Context.Channel?.WriteAndFlushAsync(responseDto).ConfigureAwait(false).GetAwaiter().GetResult(); + } + catch (Exception exception) + { + Logger.Error(exception, "Failed to process message"); + } + } + } +} diff --git a/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IO/Observers/RpcResponseObserver.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IO/Observers/RpcResponseObserver.cs new file mode 100644 index 0000000000..9286582f97 --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IO/Observers/RpcResponseObserver.cs @@ -0,0 +1,82 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.IO.Messaging.Correlation; +using Catalyst.Abstractions.Types; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Protocol.Wire; +using Dawn; +using DotNetty.Transport.Channels; +using Google.Protobuf; +using MultiFormats; +using Serilog; +using System; + +namespace Catalyst.Core.Lib.Rpc.IO +{ + public abstract class RpcResponseObserver : MessageObserverBase>, IRpcResponseObserver + where TProto : IMessage + { + private static Func, bool> FilterExpression = m => m?.Payload?.TypeUrl != null && m.Payload.TypeUrl == typeof(TProto).ShortenedProtoFullName(); + + protected RpcResponseObserver(ILogger logger, bool assertMessageNameCheck = true) : base(logger, FilterExpression) + { + if (assertMessageNameCheck) + { + Guard.Argument(typeof(TProto), nameof(TProto)).Require(t => t.IsResponseType(), + t => $"{nameof(TProto)} is not of type {MessageTypes.Response.Name}"); + } + } + + protected abstract void HandleResponse(TProto messageDto, IChannelHandlerContext channelHandlerContext, MultiAddress senderAddress, ICorrelationId correlationId); + + public void HandleResponseObserver(IMessage message, + IChannelHandlerContext channelHandlerContext, + MultiAddress sender, + ICorrelationId correlationId) + { + Guard.Argument(channelHandlerContext, nameof(channelHandlerContext)).NotNull(); + Guard.Argument(sender, nameof(sender)).NotNull(); + Guard.Argument(message, nameof(message)).NotNull("The message cannot be null"); + + HandleResponse((TProto) message, channelHandlerContext, sender, correlationId); + } + + public override void OnNext(IObserverDto messageDto) + { + Logger.Verbose("Pre Handle Message Called"); + try + { + HandleResponse(messageDto.Payload.FromProtocolMessage(), messageDto.Context, + messageDto.Payload.Address, messageDto.Payload.CorrelationId.ToCorrelationId()); + } + catch (Exception exception) + { + Logger.Error(exception, "Failed to handle response message"); + } + } + } +} diff --git a/src/Catalyst.Abstractions/Rpc/IRpcClient.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcClient.cs similarity index 86% rename from src/Catalyst.Abstractions/Rpc/IRpcClient.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcClient.cs index 2d693af39a..6280b4e83d 100644 --- a/src/Catalyst.Abstractions/Rpc/IRpcClient.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,10 +22,10 @@ #endregion using System; -using Catalyst.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; using Google.Protobuf; -namespace Catalyst.Abstractions.Rpc +namespace Catalyst.Modules.Network.Dotnetty.Rpc { public interface IRpcClient : ISocketClient { diff --git a/src/Catalyst.Abstractions/Rpc/IRpcClientFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcClientFactory.cs similarity index 80% rename from src/Catalyst.Abstractions/Rpc/IRpcClientFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcClientFactory.cs index 70d5d18bbf..1446ef75ff 100644 --- a/src/Catalyst.Abstractions/Rpc/IRpcClientFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcClientFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,13 +21,14 @@ #endregion +using Catalyst.Abstractions.Rpc; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -namespace Catalyst.Abstractions.Rpc +namespace Catalyst.Modules.Network.Dotnetty.Rpc { public interface IRpcClientFactory { - Task GetClient(X509Certificate2 certificate, IRpcClientConfig clientConfig); + Task GetClientAsync(X509Certificate2 certificate, IRpcClientConfig clientConfig); } } diff --git a/src/Catalyst.Abstractions/Rpc/IRpcNode.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcNode.cs similarity index 82% rename from src/Catalyst.Abstractions/Rpc/IRpcNode.cs rename to src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcNode.cs index 11ea432775..59f95ce321 100644 --- a/src/Catalyst.Abstractions/Rpc/IRpcNode.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcNode.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,10 @@ #endregion -using Catalyst.Abstractions.IO.Transport; +using Catalyst.Abstractions.Rpc; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; -namespace Catalyst.Abstractions.Rpc +namespace Catalyst.Modules.Network.Dotnetty.Rpc { public interface IRpcNode { diff --git a/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcServer.cs b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcServer.cs new file mode 100644 index 0000000000..7754498c3f --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/Abstractions/Rpc/IRpcServer.cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.Rpc; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Protocol.Wire; + +namespace Catalyst.Modules.Network.Dotnetty.Rpc +{ + public interface IRpcServer : IObservableMessageStreamer>, ISocket + { + IRpcServerSettings Settings { get; } + } +} diff --git a/src/Catalyst.Modules.Network.Dotnetty/Catalyst.Modules.Network.Dotnetty.csproj b/src/Catalyst.Modules.Network.Dotnetty/Catalyst.Modules.Network.Dotnetty.csproj new file mode 100644 index 0000000000..58ea726535 --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/Catalyst.Modules.Network.Dotnetty.csproj @@ -0,0 +1,17 @@ + + + net6.0 + + + + + + + + + + + + + + diff --git a/src/Catalyst.Modules.Network.Dotnetty/DotnettyNetworkModule.cs b/src/Catalyst.Modules.Network.Dotnetty/DotnettyNetworkModule.cs new file mode 100644 index 0000000000..f3d83b4900 --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/DotnettyNetworkModule.cs @@ -0,0 +1,74 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Autofac; +using Catalyst.Abstractions.P2P; +using Catalyst.Modules.Network.Dotnetty.Abstractions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.P2P; +using Catalyst.Modules.Network.Dotnetty.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.P2P.IO.Transport.Channels; +using Catalyst.Protocol.Wire; + +namespace Catalyst.Modules.Network.Dotnetty +{ + public class DotnettyNetworkModule : Module + { + protected override void Load(ContainerBuilder builder) + { + // Register IO.EventLoop + builder.RegisterType().As() + .SingleInstance(); + builder.RegisterType().As() + .SingleInstance(); + builder.RegisterType().As() + .SingleInstance(); + builder.RegisterType().As(); + builder.RegisterType().As() + .WithProperty("TcpServerHandlerWorkerThreads", 4) + .WithProperty("TcpClientHandlerWorkerThreads", 4) + .WithProperty("UdpServerHandlerWorkerThreads", 8) + .WithProperty("UdpClientHandlerWorkerThreads", 2); + + // Register P2P.IO.Transport.Channels + builder.RegisterType().As>(); + builder.RegisterType().As>(); + + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + + // Register P2P.Messaging.Broadcast + builder.RegisterType().As().SingleInstance(); + + // Register file transfer + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + } + } +} diff --git a/src/Catalyst.Modules.Network.Dotnetty/DotnettyPeerClient.cs b/src/Catalyst.Modules.Network.Dotnetty/DotnettyPeerClient.cs new file mode 100644 index 0000000000..732308c979 --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/DotnettyPeerClient.cs @@ -0,0 +1,104 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Abstractions.P2P; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Protocol.Wire; +using Google.Protobuf; +using MultiFormats; + +namespace Catalyst.Modules.Network.Dotnetty.P2P +{ + public sealed class DotnettyPeerClient : IPeerClient + { + private bool _disposed; + + private readonly IPeerSettings _peerSettings; + private readonly IBroadcastManager _broadcastManager; + private readonly IDotnettyUdpClient _dotnettyUdpClient; + + /// A factory used to build the appropriate kind of channel for a udp client. + /// + /// + public DotnettyPeerClient(IDotnettyUdpClient dotnettyUdpClient, + IBroadcastManager broadcastManager, + IPeerSettings peerSettings) + { + _dotnettyUdpClient = dotnettyUdpClient; + _broadcastManager = broadcastManager; + _peerSettings = peerSettings; + } + + public async Task StartAsync() + { + await StartAsync(CancellationToken.None).ConfigureAwait(false); + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + await _dotnettyUdpClient.StartAsync(cancellationToken).ConfigureAwait(false); + } + + public async Task SendMessageToPeersAsync(IMessage message, IEnumerable peers) + { + var protocolMessage = message.ToProtocolMessage(_peerSettings.Address); + foreach (var peer in peers) + { + await SendMessageAsync(protocolMessage, peer).ConfigureAwait(false); + } + } + + public async Task BroadcastAsync(ProtocolMessage message) + { + await _broadcastManager.BroadcastAsync(message).ConfigureAwait(false); + } + + public async Task SendMessageAsync(ProtocolMessage message, MultiAddress recipient) + { + await _dotnettyUdpClient.SendMessageAsync(message, recipient).ConfigureAwait(false); + } + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _dotnettyUdpClient.Dispose(); + } + _disposed = true; + } + } + + public void Dispose() + { + Dispose(true); + } + } +} diff --git a/src/Catalyst.Core.Lib/P2P/PeerService.cs b/src/Catalyst.Modules.Network.Dotnetty/DotnettyPeerService.cs similarity index 70% rename from src/Catalyst.Core.Lib/P2P/PeerService.cs rename to src/Catalyst.Modules.Network.Dotnetty/DotnettyPeerService.cs index a7607b6640..a0ea6f2a8e 100644 --- a/src/Catalyst.Core.Lib/P2P/PeerService.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/DotnettyPeerService.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,29 +24,29 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.IO.Observers; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.P2P.Discovery; -using Catalyst.Core.Lib.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; using Catalyst.Protocol.Wire; using Serilog; -namespace Catalyst.Core.Lib.P2P +namespace Catalyst.Modules.Network.Dotnetty.P2P { - public sealed class PeerService : UdpServer, IPeerService + public sealed class DotnettyPeerService : UdpServer, IPeerService { private readonly IEnumerable _messageHandlers; private readonly IPeerSettings _peerSettings; private readonly IHealthChecker _healthChecker; public IPeerDiscovery Discovery { get; } - public IObservable> MessageStream { get; private set; } + public IObservable MessageStream { get; private set; } - public PeerService(IUdpServerEventLoopGroupFactory udpServerEventLoopGroupFactory, - IUdpServerChannelFactory serverChannelFactory, + public DotnettyPeerService(IUdpServerEventLoopGroupFactory udpServerEventLoopGroupFactory, + IUdpServerChannelFactory serverChannelFactory, IPeerDiscovery peerDiscovery, IEnumerable messageHandlers, IPeerSettings peerSettings, @@ -62,7 +62,12 @@ public PeerService(IUdpServerEventLoopGroupFactory udpServerEventLoopGroupFactor public override async Task StartAsync() { - var observableChannel = await ChannelFactory.BuildChannelAsync(EventLoopGroupFactory, _peerSettings.BindAddress, _peerSettings.Port).ConfigureAwait(false); + await StartAsync(CancellationToken.None).ConfigureAwait(false); + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + var observableChannel = await ChannelFactory.BuildChannelAsync(EventLoopGroupFactory, _peerSettings.Address).ConfigureAwait(false); Channel = observableChannel.Channel; MessageStream = observableChannel.MessageStream; diff --git a/src/Catalyst.Core.Lib/P2P/PeerClient.cs b/src/Catalyst.Modules.Network.Dotnetty/DotnettyUdpClient.cs similarity index 56% rename from src/Catalyst.Core.Lib/P2P/PeerClient.cs rename to src/Catalyst.Modules.Network.Dotnetty/DotnettyUdpClient.cs index bf88745de7..ed195ba6e4 100644 --- a/src/Catalyst.Core.Lib/P2P/PeerClient.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/DotnettyUdpClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,25 +21,26 @@ #endregion -using System.Net; -using System.Reflection; -using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Protocol.Wire; +using MultiFormats; using Serilog; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; -namespace Catalyst.Core.Lib.P2P +namespace Catalyst.Modules.Network.Dotnetty { - public sealed class PeerClient : UdpClient, IPeerClient + public class DotnettyUdpClient : UdpClient, IDotnettyUdpClient { private readonly IPeerSettings _peerSettings; - /// A factory used to build the appropriate kind of channel for a udp client. - /// - /// - public PeerClient(IUdpClientChannelFactory clientChannelFactory, + public DotnettyUdpClient(IUdpClientChannelFactory clientChannelFactory, IUdpClientEventLoopGroupFactory eventLoopGroupFactory, IPeerSettings peerSettings) : base(clientChannelFactory, @@ -51,12 +52,19 @@ public PeerClient(IUdpClientChannelFactory clientChannelFactory, public override async Task StartAsync() { - var bindingEndpoint = new IPEndPoint(_peerSettings.BindAddress, IPEndPoint.MinPort); - var observableChannel = await ChannelFactory.BuildChannelAsync(EventLoopGroupFactory, - bindingEndpoint.Address, - bindingEndpoint.Port) - .ConfigureAwait(false); + await StartAsync(CancellationToken.None).ConfigureAwait(false); + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + var observableChannel = await ChannelFactory.BuildChannelAsync(EventLoopGroupFactory, _peerSettings.Address).ConfigureAwait(false); Channel = observableChannel.Channel; } + + public Task SendMessageAsync(ProtocolMessage message, MultiAddress recipient) + { + SendMessage(new MessageDto(message, recipient)); + return Task.CompletedTask; + } } } diff --git a/src/Catalyst.Core.Lib/FileTransfer/BaseFileTransferFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/BaseFileTransferFactory.cs similarity index 97% rename from src/Catalyst.Core.Lib/FileTransfer/BaseFileTransferFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/FileTransfer/BaseFileTransferFactory.cs index 6de227306c..4fa1d558de 100644 --- a/src/Catalyst.Core.Lib/FileTransfer/BaseFileTransferFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/BaseFileTransferFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,12 +26,12 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Catalyst.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.Types; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; using Serilog; -namespace Catalyst.Core.Lib.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.FileTransfer { /// /// diff --git a/src/Catalyst.Core.Lib/FileTransfer/BaseFileTransferInformation.cs b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/BaseFileTransferInformation.cs similarity index 91% rename from src/Catalyst.Core.Lib/FileTransfer/BaseFileTransferInformation.cs rename to src/Catalyst.Modules.Network.Dotnetty/FileTransfer/BaseFileTransferInformation.cs index 694e8f7f60..214583dc8a 100644 --- a/src/Catalyst.Core.Lib/FileTransfer/BaseFileTransferInformation.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/BaseFileTransferInformation.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,13 +25,13 @@ using System.IO; using System.Linq; using System.Threading; -using Catalyst.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Core.Lib.Config; -using Catalyst.Protocol.Peer; using DotNetty.Transport.Channels; +using MultiFormats; -namespace Catalyst.Core.Lib.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.FileTransfer { public class BaseFileTransferInformation : IFileTransferInformation { @@ -54,10 +54,10 @@ public class BaseFileTransferInformation : IFileTransferInformation public IChannel RecipientChannel { get; set; } /// - public PeerId RecipientId { get; set; } + public MultiAddress Recipient { get; set; } /// - public PeerId PeerId { get; set; } + public MultiAddress Address { get; set; } /// public CancellationToken CancellationToken { get; set; } @@ -85,8 +85,8 @@ public class BaseFileTransferInformation : IFileTransferInformation /// The correlation unique identifier. /// Name of the file. /// Size of the file. - protected BaseFileTransferInformation(PeerId peerId, - PeerId recipientId, + protected BaseFileTransferInformation(MultiAddress address, + MultiAddress recipient, IChannel recipientChannel, ICorrelationId correlationId, string fileName, @@ -95,8 +95,8 @@ protected BaseFileTransferInformation(PeerId peerId, TempPath = Path.GetTempPath() + correlationId.Id + ".tmp"; MaxChunk = (uint) Math.Max(1, (int) Math.Ceiling((double) fileSize / Constants.FileTransferChunkSize)); RecipientChannel = recipientChannel; - RecipientId = recipientId; - PeerId = peerId; + Recipient = recipient; + Address = address; CorrelationId = correlationId; FileOutputPath = fileName; ChunkIndicators = new bool[MaxChunk]; diff --git a/src/Catalyst.Core.Lib/FileTransfer/DownloadFileTransferFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/DownloadFileTransferFactory.cs similarity index 96% rename from src/Catalyst.Core.Lib/FileTransfer/DownloadFileTransferFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/FileTransfer/DownloadFileTransferFactory.cs index 33012e2e84..423d989c15 100644 --- a/src/Catalyst.Core.Lib/FileTransfer/DownloadFileTransferFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/DownloadFileTransferFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,7 @@ using System; using System.Threading.Tasks; -using Catalyst.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Config; @@ -31,7 +31,7 @@ using Catalyst.Protocol.Rpc.Node; using Serilog; -namespace Catalyst.Core.Lib.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.FileTransfer { /// /// Handles the download file transfer diff --git a/src/Catalyst.Core.Lib/FileTransfer/DownloadFileTransferInformation.cs b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/DownloadFileTransferInformation.cs similarity index 89% rename from src/Catalyst.Core.Lib/FileTransfer/DownloadFileTransferInformation.cs rename to src/Catalyst.Modules.Network.Dotnetty/FileTransfer/DownloadFileTransferInformation.cs index ef012f07d5..1b72b2e0ec 100644 --- a/src/Catalyst.Core.Lib/FileTransfer/DownloadFileTransferInformation.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/DownloadFileTransferInformation.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,13 +23,13 @@ using System; using System.IO; -using Catalyst.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Core.Lib.Config; -using Catalyst.Protocol.Peer; using DotNetty.Transport.Channels; +using MultiFormats; -namespace Catalyst.Core.Lib.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.FileTransfer { /// /// Handles the information for file downloads @@ -48,13 +48,13 @@ public sealed class DownloadFileTransferInformation : BaseFileTransferInformatio /// The correlation unique identifier. /// The file output path. /// Size of the file. - public DownloadFileTransferInformation(PeerId peerId, - PeerId recipientId, + public DownloadFileTransferInformation(MultiAddress address, + MultiAddress recipient, IChannel recipientChannel, ICorrelationId correlationGuid, string fileOutputPath, ulong fileSize) : - base(peerId, recipientId, recipientChannel, + base(address, recipient, recipientChannel, correlationGuid, fileOutputPath, fileSize) { _fileLock = new object(); diff --git a/src/Catalyst.Core.Lib/FileTransfer/UploadFileTransferFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/UploadFileTransferFactory.cs similarity index 96% rename from src/Catalyst.Core.Lib/FileTransfer/UploadFileTransferFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/FileTransfer/UploadFileTransferFactory.cs index 33d2e88425..6d338a1415 100644 --- a/src/Catalyst.Core.Lib/FileTransfer/UploadFileTransferFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/UploadFileTransferFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,13 +24,13 @@ using System; using System.Collections.Generic; using System.Threading.Tasks; -using Catalyst.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; using Catalyst.Core.Lib.Config; using Polly; using Polly.Retry; using Serilog; -namespace Catalyst.Core.Lib.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.FileTransfer { /// /// Handles and stores the upload file transfers diff --git a/src/Catalyst.Core.Lib/FileTransfer/UploadFileTransferInformation.cs b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/UploadFileTransferInformation.cs similarity index 84% rename from src/Catalyst.Core.Lib/FileTransfer/UploadFileTransferInformation.cs rename to src/Catalyst.Modules.Network.Dotnetty/FileTransfer/UploadFileTransferInformation.cs index 4f429a22d2..72f4b01d75 100644 --- a/src/Catalyst.Core.Lib/FileTransfer/UploadFileTransferInformation.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/FileTransfer/UploadFileTransferInformation.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,34 +22,34 @@ #endregion using System.IO; -using Catalyst.Abstractions.FileTransfer; +using Catalyst.Modules.Network.Dotnetty.Abstractions.FileTransfer; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; using Google.Protobuf; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; -namespace Catalyst.Core.Lib.FileTransfer +namespace Catalyst.Modules.Network.Dotnetty.FileTransfer { public sealed class UploadFileTransferInformation : BaseFileTransferInformation, IUploadFileInformation { /// Initializes a new instance of the class. /// The stream. - /// The peer identifier. + /// The peer address. /// The recipient identifier. /// The recipient channel. /// The correlation unique identifier. public UploadFileTransferInformation(Stream stream, - PeerId peerId, - PeerId recipientId, + MultiAddress address, + MultiAddress recipient, IChannel recipientChannel, ICorrelationId correlationGuid) : - base(peerId, recipientId, recipientChannel, + base(address, recipient, recipientChannel, correlationGuid, string.Empty, (ulong) stream.Length) { RandomAccessStream = stream; @@ -90,11 +90,9 @@ public IMessageDto GetUploadMessageDto(uint index) ChunkBytes = ByteString.CopyFrom(chunk), ChunkId = chunkId, CorrelationFileName = CorrelationId.Id.ToByteString() - }.ToProtocolMessage(PeerId); + }.ToProtocolMessage(Address); - return new MessageDto(transferMessage, - RecipientId - ); + return new MessageDto(transferMessage, Recipient); } public int RetryCount { get; set; } diff --git a/src/Catalyst.Core.Lib/IO/Codecs/AddressedEnvelopeToIMessageEncoder.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Codecs/AddressedEnvelopeToIMessageEncoder.cs similarity index 88% rename from src/Catalyst.Core.Lib/IO/Codecs/AddressedEnvelopeToIMessageEncoder.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Codecs/AddressedEnvelopeToIMessageEncoder.cs index 807278cd4c..b0f5baf294 100644 --- a/src/Catalyst.Core.Lib/IO/Codecs/AddressedEnvelopeToIMessageEncoder.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Codecs/AddressedEnvelopeToIMessageEncoder.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,12 +22,12 @@ #endregion using System.Collections.Generic; -using Catalyst.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; using Catalyst.Protocol.Wire; using DotNetty.Codecs; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.Codecs +namespace Catalyst.Modules.Network.Dotnetty.IO.Codecs { public sealed class AddressedEnvelopeToIMessageEncoder : MessageToMessageEncoder> diff --git a/src/Catalyst.Core.Lib/IO/EventLoop/BaseLoopGroupFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/BaseLoopGroupFactory.cs similarity index 94% rename from src/Catalyst.Core.Lib/IO/EventLoop/BaseLoopGroupFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/BaseLoopGroupFactory.cs index ed98d05325..d7dd51fbe0 100644 --- a/src/Catalyst.Core.Lib/IO/EventLoop/BaseLoopGroupFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/BaseLoopGroupFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,7 +29,7 @@ using Dawn; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.IO.EventLoop { /// /// The class keeps references of event loop groups that are created @@ -64,7 +64,7 @@ public BaseLoopGroupFactory() internal IEventLoopGroup NewEventLoopGroup(int nEventLoop) { Guard.Argument(nEventLoop).Positive(); - var eventLoopGroup = new MultithreadEventLoopGroup(nEventLoop); + MultithreadEventLoopGroup eventLoopGroup = new(nEventLoop); _eventLoopGroupList.Add(eventLoopGroup); return eventLoopGroup; } @@ -73,7 +73,7 @@ internal IEventLoopGroup NewEventLoopGroup(int nEventLoop) /// internal IEventLoopGroup NewEventLoopGroup() { - var eventLoopGroup = new MultithreadEventLoopGroup(); + MultithreadEventLoopGroup eventLoopGroup = new(); _eventLoopGroupList.Add(eventLoopGroup); return eventLoopGroup; } diff --git a/src/Catalyst.Core.Lib/IO/EventLoop/EventLoopGroupFactoryConfiguration.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/EventLoopGroupFactoryConfiguration.cs similarity index 87% rename from src/Catalyst.Core.Lib/IO/EventLoop/EventLoopGroupFactoryConfiguration.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/EventLoopGroupFactoryConfiguration.cs index 6da422a85d..4903f96fad 100644 --- a/src/Catalyst.Core.Lib/IO/EventLoop/EventLoopGroupFactoryConfiguration.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/EventLoopGroupFactoryConfiguration.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,9 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; -namespace Catalyst.Core.Lib.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.IO.EventLoop { public class EventLoopGroupFactoryConfiguration : IEventLoopGroupFactoryConfiguration { diff --git a/src/Catalyst.Core.Lib/IO/EventLoop/TcpClientEventLoopGroupFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/TcpClientEventLoopGroupFactory.cs similarity index 89% rename from src/Catalyst.Core.Lib/IO/EventLoop/TcpClientEventLoopGroupFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/TcpClientEventLoopGroupFactory.cs index 41d4b24cb9..775bb95b9e 100644 --- a/src/Catalyst.Core.Lib/IO/EventLoop/TcpClientEventLoopGroupFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/TcpClientEventLoopGroupFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.IO.EventLoop { public class TcpClientEventLoopGroupFactory : BaseLoopGroupFactory, ITcpClientEventLoopGroupFactory { diff --git a/src/Catalyst.Core.Lib/IO/EventLoop/TcpServerEventLoopGroupFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/TcpServerEventLoopGroupFactory.cs similarity index 91% rename from src/Catalyst.Core.Lib/IO/EventLoop/TcpServerEventLoopGroupFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/TcpServerEventLoopGroupFactory.cs index e12e588c4c..14bb88aff3 100644 --- a/src/Catalyst.Core.Lib/IO/EventLoop/TcpServerEventLoopGroupFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/TcpServerEventLoopGroupFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.IO.EventLoop { public class TcpServerEventLoopGroupFactory : BaseLoopGroupFactory, ITcpServerEventLoopGroupFactory { diff --git a/src/Catalyst.Core.Lib/IO/EventLoop/UdpClientEventLoopGroupFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/UdpClientEventLoopGroupFactory.cs similarity index 89% rename from src/Catalyst.Core.Lib/IO/EventLoop/UdpClientEventLoopGroupFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/UdpClientEventLoopGroupFactory.cs index 36a0fd7d67..a0b401bf6a 100644 --- a/src/Catalyst.Core.Lib/IO/EventLoop/UdpClientEventLoopGroupFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/UdpClientEventLoopGroupFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.IO.EventLoop { public class UdpClientEventLoopGroupFactory : BaseLoopGroupFactory, IUdpClientEventLoopGroupFactory { diff --git a/src/Catalyst.Core.Lib/IO/EventLoop/UdpServerEventLoopGroupFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/UdpServerEventLoopGroupFactory.cs similarity index 89% rename from src/Catalyst.Core.Lib/IO/EventLoop/UdpServerEventLoopGroupFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/UdpServerEventLoopGroupFactory.cs index 82d60fe097..8528e271d6 100644 --- a/src/Catalyst.Core.Lib/IO/EventLoop/UdpServerEventLoopGroupFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/EventLoop/UdpServerEventLoopGroupFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,10 +21,10 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.EventLoop +namespace Catalyst.Modules.Network.Dotnetty.IO.EventLoop { public class UdpServerEventLoopGroupFactory : BaseLoopGroupFactory, IUdpServerEventLoopGroupFactory { diff --git a/src/Catalyst.Core.Lib/IO/Handlers/AuthenticationHandler.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/AuthenticationHandler.cs similarity index 93% rename from src/Catalyst.Core.Lib/IO/Handlers/AuthenticationHandler.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/AuthenticationHandler.cs index 02ed606c0a..9676abb068 100644 --- a/src/Catalyst.Core.Lib/IO/Handlers/AuthenticationHandler.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/AuthenticationHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using System.Reflection; using System.Security.Authentication; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { /// /// DotNetty Handler in-charge of blocking RPC messages if the node operator is not trusted @@ -50,7 +50,7 @@ public AuthenticationHandler(IAuthenticationStrategy authenticationStrategy) /// > protected override void ChannelRead0(IChannelHandlerContext ctx, ProtocolMessage msg) { - if (_authenticationStrategy.Authenticate(msg.PeerId)) + if (_authenticationStrategy.Authenticate(msg.Address)) { ctx.FireChannelRead(msg); } diff --git a/src/Catalyst.Core.Lib/IO/Handlers/BroadcastCleanupHandler.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/BroadcastCleanupHandler.cs similarity index 89% rename from src/Catalyst.Core.Lib/IO/Handlers/BroadcastCleanupHandler.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/BroadcastCleanupHandler.cs index 41f336cb13..4dc0e07a90 100644 --- a/src/Catalyst.Core.Lib/IO/Handlers/BroadcastCleanupHandler.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/BroadcastCleanupHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,13 +22,13 @@ #endregion using System.Reflection; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { public class BroadcastCleanupHandler : InboundChannelHandlerBase { diff --git a/src/Catalyst.Core.Lib/IO/Handlers/BroadcastHandler.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/BroadcastHandler.cs similarity index 94% rename from src/Catalyst.Core.Lib/IO/Handlers/BroadcastHandler.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/BroadcastHandler.cs index 87ba01965a..5648735fc0 100644 --- a/src/Catalyst.Core.Lib/IO/Handlers/BroadcastHandler.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/BroadcastHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,13 +22,13 @@ #endregion using System.Reflection; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { /// /// Channel Gossip Pipeline diff --git a/src/Catalyst.Core.Lib/IO/Handlers/CorrelatableHandler.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/CorrelatableHandler.cs similarity index 87% rename from src/Catalyst.Core.Lib/IO/Handlers/CorrelatableHandler.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/CorrelatableHandler.cs index a447cd4d6a..e77a880843 100644 --- a/src/Catalyst.Core.Lib/IO/Handlers/CorrelatableHandler.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/CorrelatableHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,13 +24,13 @@ using System; using System.Threading.Tasks; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { public sealed class CorrelatableHandler : OutboundChannelHandlerBase> @@ -45,13 +45,13 @@ public CorrelatableHandler(T messageCorrelationManager) } /// - protected override Task WriteAsync0(IChannelHandlerContext context, IMessageDto message) + protected override Task Write0Async(IChannelHandlerContext context, IMessageDto message) { if (message.Content.TypeUrl.EndsWith(MessageTypes.Request.Name)) { _messageCorrelationManager.AddPendingRequest(new CorrelatableMessage { - Recipient = message.RecipientPeerIdentifier, + Recipient = message.RecipientAddress, Content = message.Content, SentAt = DateTimeOffset.UtcNow }); diff --git a/src/Catalyst.Core.Lib/IO/Handlers/CorrelationHandler.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/CorrelationHandler.cs similarity index 96% rename from src/Catalyst.Core.Lib/IO/Handlers/CorrelationHandler.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/CorrelationHandler.cs index 77f8a60639..5bc390d449 100644 --- a/src/Catalyst.Core.Lib/IO/Handlers/CorrelationHandler.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/CorrelationHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using DotNetty.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { public sealed class CorrelationHandler : InboundChannelHandlerBase diff --git a/src/Catalyst.Core.Lib/IO/Handlers/FlushPipelineHandler.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/FlushPipelineHandler.cs similarity index 89% rename from src/Catalyst.Core.Lib/IO/Handlers/FlushPipelineHandler.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/FlushPipelineHandler.cs index ee199aebdc..3e9aa2c497 100644 --- a/src/Catalyst.Core.Lib/IO/Handlers/FlushPipelineHandler.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/FlushPipelineHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,14 +26,14 @@ using DotNetty.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { public sealed class FlushPipelineHandler : OutboundChannelHandlerBase { // ReSharper disable once StaticMemberInGenericType private static readonly ILogger Logger = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType); - protected override Task WriteAsync0(IChannelHandlerContext ctx, T msg) + protected override Task Write0Async(IChannelHandlerContext ctx, T msg) { Logger.Verbose("Received {msg}", msg); return ctx.WriteAndFlushAsync(msg); diff --git a/src/Catalyst.Core.Lib/IO/Handlers/InboundChannelHandlerBase.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/InboundChannelHandlerBase.cs similarity index 96% rename from src/Catalyst.Core.Lib/IO/Handlers/InboundChannelHandlerBase.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/InboundChannelHandlerBase.cs index 35dd5cd039..38f17b8d3c 100644 --- a/src/Catalyst.Core.Lib/IO/Handlers/InboundChannelHandlerBase.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/InboundChannelHandlerBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,7 +26,7 @@ using DotNetty.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { public abstract class InboundChannelHandlerBase : ChannelHandlerAdapter { diff --git a/src/Catalyst.Core.Lib/IO/Handlers/ObservableServiceHandler.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/ObservableServiceHandler.cs similarity index 79% rename from src/Catalyst.Core.Lib/IO/Handlers/ObservableServiceHandler.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/ObservableServiceHandler.cs index d6c187907a..6ef8576b63 100644 --- a/src/Catalyst.Core.Lib/IO/Handlers/ObservableServiceHandler.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/ObservableServiceHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,33 +27,31 @@ using System.Reactive.Subjects; using System.Reflection; using Catalyst.Abstractions.IO.Handlers; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { /// - /// This handler terminates DotNetty involvement and passes service messages into rx land, + /// This handler terminates DotNetty involvement and passes repository messages into rx land, /// by this point all messages should be treated as genuine and sanitised. /// - public sealed class ObservableServiceHandler : InboundChannelHandlerBase, IObservableServiceHandler + public sealed class ObservableServiceHandler : InboundChannelHandlerBase, IObservableServiceHandler { - private readonly ReplaySubject> _messageSubject; + private readonly ReplaySubject _messageSubject; public ObservableServiceHandler(IScheduler scheduler = null) : base(Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType)) { var observableScheduler = scheduler ?? Scheduler.Default; - _messageSubject = new ReplaySubject>(1, observableScheduler); + _messageSubject = new ReplaySubject(1, observableScheduler); MessageStream = _messageSubject.AsObservable(); } public override bool IsSharable => true; - public IObservable> MessageStream { get; } + public IObservable MessageStream { get; } public override void ExceptionCaught(IChannelHandlerContext context, Exception e) { @@ -81,8 +79,7 @@ public void Dispose() protected override void ChannelRead0(IChannelHandlerContext ctx, ProtocolMessage message) { Logger.Verbose("Received {message}", message); - var contextAny = new ObserverDto(ctx, message); - _messageSubject.OnNext(contextAny); + _messageSubject.OnNext(message); ctx.FireChannelRead(message); } } diff --git a/src/Catalyst.Core.Lib/IO/Handlers/OutboundChannelHandlerBase.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/OutboundChannelHandlerBase.cs similarity index 88% rename from src/Catalyst.Core.Lib/IO/Handlers/OutboundChannelHandlerBase.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/OutboundChannelHandlerBase.cs index f733eaba55..e27011daa7 100644 --- a/src/Catalyst.Core.Lib/IO/Handlers/OutboundChannelHandlerBase.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/OutboundChannelHandlerBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,7 @@ using DotNetty.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { /// /// OutboundChannel Handler is similar to Dot Netty's simple inbound channel handler, except it removes some redundant double cast operations. @@ -39,7 +39,7 @@ public abstract class OutboundChannelHandlerBase : ChannelHandlerAdapter private static readonly ILogger Logger = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType); /// - /// Does check to see if it can process the msg, if object is T thn it fires the inheritor WriteAsync0 + /// Does check to see if it can process the msg, if object is T thn it fires the inheritor Write0Async /// /// /// @@ -51,7 +51,7 @@ public override Task WriteAsync(IChannelHandlerContext ctx, object msg) { if (msg is T msg1) { - writeTask = WriteAsync0(ctx, msg1); + writeTask = Write0Async(ctx, msg1); } } catch (Exception e) @@ -68,8 +68,6 @@ public override Task WriteAsync(IChannelHandlerContext ctx, object msg) /// /// /// - - // ReSharper disable once VSTHRD200 - protected abstract Task WriteAsync0(IChannelHandlerContext ctx, T msg); + protected abstract Task Write0Async(IChannelHandlerContext ctx, T msg); } } diff --git a/src/Catalyst.Core.Lib/IO/Handlers/PeerIdValidationHandler.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/PeerIdValidationHandler.cs similarity index 90% rename from src/Catalyst.Core.Lib/IO/Handlers/PeerIdValidationHandler.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/PeerIdValidationHandler.cs index a3b1f766df..1236d3e56a 100644 --- a/src/Catalyst.Core.Lib/IO/Handlers/PeerIdValidationHandler.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/PeerIdValidationHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,7 @@ using DotNetty.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { public sealed class PeerIdValidationHandler : SimpleChannelInboundHandler { @@ -39,7 +39,7 @@ public sealed class PeerIdValidationHandler : SimpleChannelInboundHandler * @@ -23,16 +23,16 @@ using System.Reflection; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.KeySigner; using Catalyst.Core.Lib.Extensions.Protocol.Wire; -using Catalyst.Core.Lib.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { public sealed class ProtocolMessageSignHandler : OutboundChannelHandlerBase> { @@ -53,11 +53,11 @@ public ProtocolMessageSignHandler(IKeySigner keySigner, SigningContext signingCo /// /// /// - protected override Task WriteAsync0(IChannelHandlerContext context, IMessageDto message) + protected override Task Write0Async(IChannelHandlerContext context, IMessageDto message) { Logger.Verbose("Signing message {message}", message); var protocolMessageSigned = message.Content.Sign(_keySigner, _signingContext); - var signedDto = new SignedMessageDto(protocolMessageSigned, message.RecipientPeerIdentifier); + SignedMessageDto signedDto = new(protocolMessageSigned, message.RecipientAddress); return context.WriteAsync(signedDto); } } diff --git a/src/Catalyst.Core.Lib/IO/Handlers/ProtocolMessageVerifyHandler.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/ProtocolMessageVerifyHandler.cs similarity index 87% rename from src/Catalyst.Core.Lib/IO/Handlers/ProtocolMessageVerifyHandler.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/ProtocolMessageVerifyHandler.cs index 8260b233bf..6b01ac53e6 100644 --- a/src/Catalyst.Core.Lib/IO/Handlers/ProtocolMessageVerifyHandler.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/ProtocolMessageVerifyHandler.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,10 +26,10 @@ using Catalyst.Core.Lib.Extensions; using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; -using Google.Protobuf; +using MultiFormats; using Serilog; -namespace Catalyst.Core.Lib.IO.Handlers +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers { public sealed class ProtocolMessageVerifyHandler : InboundChannelHandlerBase { @@ -71,13 +71,16 @@ private bool Verify(ProtocolMessage signedMessage) } var sig = signedMessage.Signature.RawBytes.ToByteArray(); - var pub = signedMessage.PeerId.PublicKey.ToByteArray(); + MultiAddress address = new(signedMessage.Address); + var pub = address.GetPublicKeyBytes(); + var signature = _keySigner.CryptoContext.GetSignatureFromBytes(sig, pub); var messageWithoutSig = signedMessage.Clone(); messageWithoutSig.Signature = null; - return _keySigner.Verify(signature, messageWithoutSig.ToByteArray(), - signedMessage.Signature.SigningContext); + var verified = _keySigner.Verify(signature, messageWithoutSig, signedMessage.Signature.SigningContext); + + return verified; } } } diff --git a/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/RpcObservableServiceHandler.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/RpcObservableServiceHandler.cs new file mode 100644 index 0000000000..9e8bfa1e76 --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Handlers/RpcObservableServiceHandler.cs @@ -0,0 +1,89 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Reflection; +using Catalyst.Abstractions.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Protocol.Wire; +using DotNetty.Transport.Channels; +using Serilog; + +namespace Catalyst.Modules.Network.Dotnetty.IO.Handlers +{ + /// + /// This handler terminates DotNetty involvement and passes repository messages into rx land, + /// by this point all messages should be treated as genuine and sanitised. + /// + public sealed class RpcObservableServiceHandler : InboundChannelHandlerBase, IObservableServiceHandler> + { + private readonly ReplaySubject> _messageSubject; + + public RpcObservableServiceHandler(IScheduler scheduler = null) + : base(Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType)) + { + var observableScheduler = scheduler ?? Scheduler.Default; + _messageSubject = new ReplaySubject>(1, observableScheduler); + MessageStream = _messageSubject.AsObservable(); + } + + public override bool IsSharable => true; + + public IObservable> MessageStream { get; } + + public override void ExceptionCaught(IChannelHandlerContext context, Exception e) + { + Logger.Error(e, "Error in ObservableServiceHandler"); + } + + private void Dispose(bool disposing) + { + if (disposing) + { + _messageSubject?.Dispose(); + } + } + + public void Dispose() + { + Dispose(true); + } + + /// + /// Reads the channel once accepted and pushed into a stream. + /// + /// + /// + protected override void ChannelRead0(IChannelHandlerContext ctx, ProtocolMessage message) + { + Logger.Verbose("Received {message}", message); + ObserverDto contextAny = new(ctx, message); + _messageSubject.OnNext(contextAny); + ctx.FireChannelRead(message); + } + } +} diff --git a/src/Catalyst.Core.Lib/IO/Messaging/Dto/BaseMessageDto.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/BaseMessageDto.cs similarity index 60% rename from src/Catalyst.Core.Lib/IO/Messaging/Dto/BaseMessageDto.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/BaseMessageDto.cs index cd4b2eba73..b090ae1fa6 100644 --- a/src/Catalyst.Core.Lib/IO/Messaging/Dto/BaseMessageDto.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/BaseMessageDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,41 +22,41 @@ #endregion using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Protocol.Peer; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; using Dawn; using DotNetty.Transport.Channels; using Google.Protobuf; +using MultiFormats; -namespace Catalyst.Core.Lib.IO.Messaging.Dto +namespace Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto { public class BaseMessageDto : DefaultAddressedEnvelope, IMessageDto where T : IMessage { public ICorrelationId CorrelationId { get; } - public PeerId RecipientPeerIdentifier { get; } - public PeerId SenderPeerIdentifier { get; } + public MultiAddress RecipientAddress { get; } + public MultiAddress SenderAddress { get; } /// /// Data transfer object to wrap up all parameters for sending protocol messages into a MessageFactors. /// /// - /// - /// + /// + /// /// protected BaseMessageDto(T content, - PeerId senderPeerIdentifier, - PeerId recipientPeerIdentifier, + MultiAddress sender, + MultiAddress recipient, ICorrelationId correlationId) - : base(content, senderPeerIdentifier.IpEndPoint, recipientPeerIdentifier.IpEndPoint) + : base(content, sender.GetIPEndPoint(), recipient.GetIPEndPoint()) { - Guard.Argument(senderPeerIdentifier.IpEndPoint.Address, nameof(senderPeerIdentifier.IpEndPoint.Address)).NotNull(); - Guard.Argument(recipientPeerIdentifier.IpEndPoint.Address, - nameof(recipientPeerIdentifier.IpEndPoint.Address)).NotNull(); + Guard.Argument(sender.GetIPEndPoint().Address).NotNull(); + Guard.Argument(recipient.GetIPEndPoint().Address).NotNull(); CorrelationId = correlationId; - SenderPeerIdentifier = senderPeerIdentifier; - RecipientPeerIdentifier = recipientPeerIdentifier; + SenderAddress = sender; + RecipientAddress = recipient; } } } diff --git a/src/Catalyst.Core.Lib/IO/Messaging/Dto/MessageDto.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/MessageDto.cs similarity index 77% rename from src/Catalyst.Core.Lib/IO/Messaging/Dto/MessageDto.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/MessageDto.cs index 959796db97..f24ce4fe82 100644 --- a/src/Catalyst.Core.Lib/IO/Messaging/Dto/MessageDto.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/MessageDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,10 +22,10 @@ #endregion using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; +using MultiFormats; -namespace Catalyst.Core.Lib.IO.Messaging.Dto +namespace Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto { public sealed class MessageDto : BaseMessageDto { @@ -34,9 +34,6 @@ public sealed class MessageDto : BaseMessageDto /// /// /// - public MessageDto(ProtocolMessage content, - PeerId recipientPeerIdentifier) - : base(content, content.PeerId, recipientPeerIdentifier, - new CorrelationId(content.CorrelationId)) { } + public MessageDto(ProtocolMessage content, MultiAddress recipient) : base(content, content.Address, recipient, new CorrelationId(content.CorrelationId)) { } } } diff --git a/src/Catalyst.Core.Lib/IO/Messaging/Dto/ObserverDto.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/ObserverDto.cs similarity index 87% rename from src/Catalyst.Core.Lib/IO/Messaging/Dto/ObserverDto.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/ObserverDto.cs index 06c6589ed4..6c57d4b2b0 100644 --- a/src/Catalyst.Core.Lib/IO/Messaging/Dto/ObserverDto.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/ObserverDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,11 +21,11 @@ #endregion -using Catalyst.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.Messaging.Dto +namespace Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto { public sealed class ObserverDto : IObserverDto diff --git a/src/Catalyst.Core.Lib/IO/Messaging/Dto/SignedMessageDto.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/SignedMessageDto.cs similarity index 84% rename from src/Catalyst.Core.Lib/IO/Messaging/Dto/SignedMessageDto.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/SignedMessageDto.cs index 6d58d43ce4..0bf358aafa 100644 --- a/src/Catalyst.Core.Lib/IO/Messaging/Dto/SignedMessageDto.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Messaging/Dto/SignedMessageDto.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,10 +22,10 @@ #endregion using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; +using MultiFormats; -namespace Catalyst.Core.Lib.IO.Messaging.Dto +namespace Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto { public sealed class SignedMessageDto : BaseMessageDto { @@ -35,8 +35,8 @@ public sealed class SignedMessageDto : BaseMessageDto /// /// public SignedMessageDto(ProtocolMessage content, - PeerId recipientPeerIdentifier) - : base(content, content.PeerId, recipientPeerIdentifier, + MultiAddress recipientPeerIdentifier) + : base(content, content.Address, recipientPeerIdentifier, new CorrelationId(content.CorrelationId)) { } } } diff --git a/src/Catalyst.Core.Lib/IO/Transport/Bootstrapping/Bootstrap.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Bootstrapping/Bootstrap.cs similarity index 91% rename from src/Catalyst.Core.Lib/IO/Transport/Bootstrapping/Bootstrap.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Bootstrapping/Bootstrap.cs index 7d73615dc6..0423fce433 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/Bootstrapping/Bootstrap.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Bootstrapping/Bootstrap.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,8 +24,8 @@ using System.Net; using System.Net.Sockets; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.Transport.Bootstrapping; -using Catalyst.Core.Lib.Util; +using Catalyst.Abstractions.Lib.Util; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Bootstrapping; using DotNetty.Transport.Channels; using Polly; using Polly.Retry; diff --git a/src/Catalyst.Core.Lib/IO/Transport/Bootstrapping/ServerBootstrap.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Bootstrapping/ServerBootstrap.cs similarity index 91% rename from src/Catalyst.Core.Lib/IO/Transport/Bootstrapping/ServerBootstrap.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Bootstrapping/ServerBootstrap.cs index 906c160402..0eed4f2f03 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/Bootstrapping/ServerBootstrap.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Bootstrapping/ServerBootstrap.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,8 +24,8 @@ using System.Net; using System.Net.Sockets; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.Transport.Bootstrapping; -using Catalyst.Core.Lib.Util; +using Catalyst.Abstractions.Lib.Util; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Bootstrapping; using DotNetty.Transport.Channels; using Polly; using Polly.Retry; diff --git a/src/Catalyst.Core.Lib/IO/Transport/Channels/ChannelInitializerBase.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ChannelInitializerBase.cs similarity index 94% rename from src/Catalyst.Core.Lib/IO/Transport/Channels/ChannelInitializerBase.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ChannelInitializerBase.cs index 95c2707274..08406c2b0e 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/Channels/ChannelInitializerBase.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ChannelInitializerBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,13 +26,13 @@ using System.Linq; using System.Net; using System.Security.Cryptography.X509Certificates; -using Catalyst.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; using Dawn; using DotNetty.Handlers.Logging; using DotNetty.Handlers.Tls; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels { public abstract class ChannelInitializerBase : ChannelInitializer diff --git a/src/Catalyst.Core.Lib/IO/Transport/Channels/ClientChannelInitializerBase.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ClientChannelInitializerBase.cs similarity index 92% rename from src/Catalyst.Core.Lib/IO/Transport/Channels/ClientChannelInitializerBase.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ClientChannelInitializerBase.cs index c9539a2f03..7289d753d7 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/Channels/ClientChannelInitializerBase.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ClientChannelInitializerBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,12 +26,12 @@ using System.Net; using System.Net.Security; using System.Security.Cryptography.X509Certificates; -using Catalyst.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; using DotNetty.Common.Utilities; using DotNetty.Handlers.Tls; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels { public sealed class ClientChannelInitializerBase : ChannelInitializerBase where T : IChannel { diff --git a/src/Catalyst.Core.Lib/IO/Transport/Channels/ObservableChannel.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ObservableChannel.cs similarity index 71% rename from src/Catalyst.Core.Lib/IO/Transport/Channels/ObservableChannel.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ObservableChannel.cs index 8dc0b7e89d..9aeb02b495 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/Channels/ObservableChannel.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ObservableChannel.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,17 +23,15 @@ using System; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Transport.Channels; -using Catalyst.Protocol.Wire; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; using Dawn; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels { - public sealed class ObservableChannel : IObservableChannel + public class ObservableChannel : IObservableChannel { - public ObservableChannel(IObservable> messageStream, IChannel channel) + public ObservableChannel(IObservable messageStream, IChannel channel) { Guard.Argument(messageStream, nameof(messageStream)).NotNull(); @@ -43,6 +41,6 @@ public ObservableChannel(IObservable> messageStrea public IChannel Channel { get; } public Task StartAsync() { return Task.CompletedTask; } - public IObservable> MessageStream { get; } + public IObservable MessageStream { get; } } } diff --git a/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/P2PObservableChannel .cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/P2PObservableChannel .cs new file mode 100644 index 0000000000..9e79cbd092 --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/P2PObservableChannel .cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Protocol.Wire; +using Dawn; +using DotNetty.Transport.Channels; + +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels +{ + public sealed class P2PObservableChannel : ObservableChannel, IP2PObservableChannel + { + public P2PObservableChannel(IObservable messageStream, IChannel channel) : base(messageStream, channel) { } + } +} diff --git a/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/RpcObservableChannel .cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/RpcObservableChannel .cs new file mode 100644 index 0000000000..4c395cff62 --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/RpcObservableChannel .cs @@ -0,0 +1,36 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Protocol.Wire; +using DotNetty.Transport.Channels; + +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels +{ + public sealed class RpcObservableChannel : ObservableChannel>, IRpcObservableChannel + { + public RpcObservableChannel(IObservable> messageStream, IChannel channel) : base(messageStream, channel) { } + } +} diff --git a/src/Catalyst.Core.Lib/IO/Transport/Channels/ServerChannelInitializerBase.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ServerChannelInitializerBase.cs similarity index 91% rename from src/Catalyst.Core.Lib/IO/Transport/Channels/ServerChannelInitializerBase.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ServerChannelInitializerBase.cs index 646ad47170..d2b47c1041 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/Channels/ServerChannelInitializerBase.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/ServerChannelInitializerBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,12 +25,12 @@ using System.Collections.Generic; using System.Net; using System.Security.Cryptography.X509Certificates; -using Catalyst.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; using DotNetty.Common.Utilities; using DotNetty.Handlers.Tls; using DotNetty.Transport.Channels; -namespace Catalyst.Core.Lib.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels { internal sealed class ServerChannelInitializerBase : ChannelInitializerBase where T : IChannel diff --git a/src/Catalyst.Core.Lib/IO/Transport/Channels/TcpClientChannelFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/TcpClientChannelFactory.cs similarity index 71% rename from src/Catalyst.Core.Lib/IO/Transport/Channels/TcpClientChannelFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/TcpClientChannelFactory.cs index 72ac91a73f..0e88724f60 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/Channels/TcpClientChannelFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/TcpClientChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,38 +23,37 @@ using System; using System.Collections.Generic; -using System.Net; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; +using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Transport.Bootstrapping; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; using DotNetty.Handlers.Logging; using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Sockets; +using MultiFormats; -namespace Catalyst.Core.Lib.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels { - public abstract class TcpClientChannelFactory : ITcpClientChannelFactory + public abstract class TcpClientChannelFactory : ITcpClientChannelFactory { private readonly int _backLogValue; protected abstract Func> HandlerGenerationFunction { get; } protected TcpClientChannelFactory(int backLogValue = 100) { _backLogValue = backLogValue; } - public abstract Task BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + public abstract Task> BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, + MultiAddress address, X509Certificate2 certificate = null); protected async Task BootstrapAsync(IEventLoopGroupFactory handlerEventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + MultiAddress address, X509Certificate2 certificate = null) { - var channelHandler = new ClientChannelInitializerBase(HandlerGenerationFunction, + ClientChannelInitializerBase channelHandler = new(HandlerGenerationFunction, handlerEventLoopGroupFactory, - targetAddress, + address.GetIpAddress(), certificate); return await new Bootstrap() @@ -63,7 +62,7 @@ protected async Task BootstrapAsync(IEventLoopGroupFactory handlerEven .Option(ChannelOption.SoBacklog, _backLogValue) .Handler(new LoggingHandler(LogLevel.DEBUG)) .Handler(channelHandler) - .ConnectAsync(targetAddress, targetPort) + .ConnectAsync(address.GetIpAddress(), address.GetPort()) .ConfigureAwait(false); } } diff --git a/src/Catalyst.Core.Lib/IO/Transport/Channels/TcpServerChannelFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/TcpServerChannelFactory.cs similarity index 73% rename from src/Catalyst.Core.Lib/IO/Transport/Channels/TcpServerChannelFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/TcpServerChannelFactory.cs index a85e9b782e..51e81af900 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/Channels/TcpServerChannelFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/TcpServerChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,19 +23,20 @@ using System; using System.Collections.Generic; -using System.Net; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; +using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Transport.Bootstrapping; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; using DotNetty.Handlers.Logging; using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Sockets; +using MultiFormats; -namespace Catalyst.Core.Lib.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels { - public abstract class TcpServerChannelFactory : ITcpServerChannelFactory + public abstract class TcpServerChannelFactory : ITcpServerChannelFactory { private readonly int _backLogValue; @@ -46,19 +47,17 @@ protected TcpServerChannelFactory(int backLogValue = 100) _backLogValue = backLogValue; } - public abstract Task BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + public abstract Task> BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, + MultiAddress address, X509Certificate2 certificate = null); protected async Task BootstrapAsync(IEventLoopGroupFactory handlerEventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + MultiAddress address, X509Certificate2 certificate) { var supervisorLoopGroup = ((ITcpServerEventLoopGroupFactory) handlerEventLoopGroupFactory) .GetOrCreateSupervisorEventLoopGroup(); - var channelHandler = new ServerChannelInitializerBase(HandlerGenerationFunction, handlerEventLoopGroupFactory, certificate); + ServerChannelInitializerBase channelHandler = new(HandlerGenerationFunction, handlerEventLoopGroupFactory, certificate); return await new ServerBootstrap() .Group(handlerEventLoopGroupFactory.GetOrCreateSocketIoEventLoopGroup(), supervisorLoopGroup) @@ -66,7 +65,7 @@ protected async Task BootstrapAsync(IEventLoopGroupFactory handlerEven .Option(ChannelOption.SoBacklog, _backLogValue) .Handler(new LoggingHandler(LogLevel.DEBUG)) .ChildHandler(channelHandler) - .BindAsync(targetAddress, targetPort) + .BindAsync(address.GetIpAddress(), address.GetPort()) .ConfigureAwait(false); } } diff --git a/src/Catalyst.Core.Lib/IO/Transport/Channels/UdpChannelFactoryBase.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/UdpChannelFactoryBase.cs similarity index 86% rename from src/Catalyst.Core.Lib/IO/Transport/Channels/UdpChannelFactoryBase.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/UdpChannelFactoryBase.cs index 042744e0bc..c88aeb4455 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/Channels/UdpChannelFactoryBase.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/UdpChannelFactoryBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,13 +26,13 @@ using System.Net; using System.Net.Sockets; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; using Catalyst.Core.Lib.IO.Transport.Bootstrapping; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; using DotNetty.Handlers.Logging; using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Sockets; -namespace Catalyst.Core.Lib.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels { public abstract class UdpChannelFactoryBase { @@ -42,7 +42,7 @@ protected async Task BootStrapChannelAsync(IEventLoopGroupFactory hand IPAddress address, int port) { - var channelHandler = new ServerChannelInitializerBase(HandlerGenerationFunction, handlerEventLoopGroupFactory); + ServerChannelInitializerBase channelHandler = new(HandlerGenerationFunction, handlerEventLoopGroupFactory); return await new Bootstrap() .Group(handlerEventLoopGroupFactory.GetOrCreateSocketIoEventLoopGroup()) diff --git a/src/Catalyst.Core.Lib/IO/Transport/Channels/UdpClientChannelFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/UdpClientChannelFactory.cs similarity index 68% rename from src/Catalyst.Core.Lib/IO/Transport/Channels/UdpClientChannelFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/UdpClientChannelFactory.cs index bb652c1076..4e21d3c764 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/Channels/UdpClientChannelFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/UdpClientChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,24 +21,23 @@ #endregion -using System.Net; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using MultiFormats; -namespace Catalyst.Core.Lib.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels { - public abstract class UdpClientChannelFactory : UdpChannelFactoryBase, IUdpClientChannelFactory + public abstract class UdpClientChannelFactory : UdpChannelFactoryBase, IUdpClientChannelFactory { /// /// /// Ignored /// Ignored /// - public abstract Task BuildChannelAsync(IEventLoopGroupFactory handlerEventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + public abstract Task> BuildChannelAsync(IEventLoopGroupFactory handlerEventLoopGroupFactory, + MultiAddress address, X509Certificate2 certificate = null); } } diff --git a/src/Catalyst.Core.Lib/IO/Transport/Channels/UdpServerChannelFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/UdpServerChannelFactory.cs similarity index 63% rename from src/Catalyst.Core.Lib/IO/Transport/Channels/UdpServerChannelFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/UdpServerChannelFactory.cs index cae5af99d2..65e39b4d7e 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/Channels/UdpServerChannelFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/Channels/UdpServerChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,19 +21,18 @@ #endregion -using System.Net; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using MultiFormats; -namespace Catalyst.Core.Lib.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels { - public abstract class UdpServerChannelFactory : UdpChannelFactoryBase, IUdpServerChannelFactory + public abstract class UdpServerChannelFactory : UdpChannelFactoryBase, IUdpServerChannelFactory { - public abstract Task BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + public abstract Task> BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, + MultiAddress address, X509Certificate2 certificate = null); } } diff --git a/src/Catalyst.Core.Lib/IO/Transport/ClientBase.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/ClientBase.cs similarity index 58% rename from src/Catalyst.Core.Lib/IO/Transport/ClientBase.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/ClientBase.cs index c2f762aa99..6aac312b34 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/ClientBase.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/ClientBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,19 +21,27 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Transport; -using Catalyst.Abstractions.IO.Transport.Channels; +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; using Google.Protobuf; using Serilog; -namespace Catalyst.Core.Lib.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport { - public abstract class ClientBase : SocketBase, ISocketClient + public abstract class ClientBase : SocketBase, ISocketClient { - protected ClientBase(IChannelFactory channelFactory, ILogger logger, IEventLoopGroupFactory handlerEventEventLoopGroupFactory) - : base(channelFactory, logger, handlerEventEventLoopGroupFactory) { } + private ILogger _logger; + + protected ClientBase(IChannelFactory channelFactory, + ILogger logger, + IEventLoopGroupFactory handlerEventEventLoopGroupFactory) + : base(channelFactory, logger, handlerEventEventLoopGroupFactory) + { + _logger = logger; + } public virtual void SendMessage(IMessageDto message) where T : IMessage { diff --git a/src/Catalyst.Core.Lib/IO/Transport/SocketBase.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/SocketBase.cs similarity index 75% rename from src/Catalyst.Core.Lib/IO/Transport/SocketBase.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/SocketBase.cs index f0350d5354..ba10a7454a 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/SocketBase.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/SocketBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,17 +24,17 @@ using System; using System.Threading; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport; -using Catalyst.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; using DotNetty.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport { - public abstract class SocketBase : ISocket + public abstract class SocketBase : ISocket { - protected readonly IChannelFactory ChannelFactory; + protected readonly IChannelFactory ChannelFactory; private readonly ILogger _logger; private int _disposeCounter; protected readonly IEventLoopGroupFactory EventLoopGroupFactory; @@ -42,7 +42,7 @@ public abstract class SocketBase : ISocket public IChannel Channel { get; protected set; } - protected SocketBase(IChannelFactory channelFactory, ILogger logger, IEventLoopGroupFactory eventLoopGroupFactory) + protected SocketBase(IChannelFactory channelFactory, ILogger logger, IEventLoopGroupFactory eventLoopGroupFactory) { ChannelFactory = channelFactory; _logger = logger; @@ -60,7 +60,9 @@ protected virtual void Dispose(bool disposing) { return; } - + + _logger.Debug("Stacktrace: " + new System.Diagnostics.StackTrace()); + _logger.Debug($"Disposing{GetType().Name}"); try diff --git a/src/Catalyst.Core.Lib/IO/Transport/SocketClientRegistry.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/SocketClientRegistry.cs similarity index 96% rename from src/Catalyst.Core.Lib/IO/Transport/SocketClientRegistry.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/SocketClientRegistry.cs index 39ce9be7fc..facfcd70ac 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/SocketClientRegistry.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/SocketClientRegistry.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,11 +29,11 @@ using System.Reactive.Linq; using System.Reactive.Subjects; using Catalyst.Abstractions.IO.Events; -using Catalyst.Abstractions.IO.Transport; using Catalyst.Core.Lib.IO.Events; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; using Dawn; -namespace Catalyst.Core.Lib.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport { public sealed class SocketClientRegistry : ISocketClientRegistry, IDisposable diff --git a/src/Catalyst.Core.Lib/IO/Transport/TcpClient.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/TcpClient.cs similarity index 62% rename from src/Catalyst.Core.Lib/IO/Transport/TcpClient.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/TcpClient.cs index 31cde9d130..a0a658ae94 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/TcpClient.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/TcpClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,16 +21,16 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport; -using Catalyst.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport { - public abstract class TcpClient : ClientBase, ITcpClient + public abstract class TcpClient : ClientBase, ITcpClient { - protected TcpClient(ITcpClientChannelFactory channelFactory, ILogger logger, ITcpClientEventLoopGroupFactory loopGroupFactory) + protected TcpClient(ITcpClientChannelFactory channelFactory, ILogger logger, ITcpClientEventLoopGroupFactory loopGroupFactory) : base(channelFactory, logger, loopGroupFactory) { } } } diff --git a/src/Catalyst.Core.Lib/IO/Transport/TcpServer.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/TcpServer.cs similarity index 71% rename from src/Catalyst.Core.Lib/IO/Transport/TcpServer.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/TcpServer.cs index aa012c76fd..443e731790 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/TcpServer.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/TcpServer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,16 +21,16 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport; -using Catalyst.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport { - public abstract class TcpServer : SocketBase, ITcpServer + public abstract class TcpServer : SocketBase, ITcpServer { - protected TcpServer(ITcpServerChannelFactory tcpChannelFactory, + protected TcpServer(ITcpServerChannelFactory tcpChannelFactory, ILogger logger, IEventLoopGroupFactory eventLoopGroupFactory) : base(tcpChannelFactory, logger, eventLoopGroupFactory) { } diff --git a/src/Catalyst.Core.Lib/IO/Transport/UdpClient.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/UdpClient.cs similarity index 62% rename from src/Catalyst.Core.Lib/IO/Transport/UdpClient.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/UdpClient.cs index ceb82de43e..4762f3209b 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/UdpClient.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/UdpClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,16 +21,16 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport; -using Catalyst.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport { - public abstract class UdpClient : ClientBase, IUdpClient + public abstract class UdpClient : ClientBase, IUdpClient { - protected UdpClient(IUdpClientChannelFactory clientChannelFactory, ILogger logger, IUdpClientEventLoopGroupFactory eventLoopGroupFactory) + protected UdpClient(IUdpClientChannelFactory clientChannelFactory, ILogger logger, IUdpClientEventLoopGroupFactory eventLoopGroupFactory) : base(clientChannelFactory, logger, eventLoopGroupFactory) { } } } diff --git a/src/Catalyst.Core.Lib/IO/Transport/UdpServer.cs b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/UdpServer.cs similarity index 68% rename from src/Catalyst.Core.Lib/IO/Transport/UdpServer.cs rename to src/Catalyst.Modules.Network.Dotnetty/IO/Transport/UdpServer.cs index 3fb3d43bd6..1987953504 100644 --- a/src/Catalyst.Core.Lib/IO/Transport/UdpServer.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/IO/Transport/UdpServer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,16 +21,16 @@ #endregion -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport; -using Catalyst.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; using Serilog; -namespace Catalyst.Core.Lib.IO.Transport +namespace Catalyst.Modules.Network.Dotnetty.IO.Transport { - public abstract class UdpServer : SocketBase, IUdpServer + public abstract class UdpServer : SocketBase, IUdpServer { - protected UdpServer(IUdpServerChannelFactory serverChannelFactory, + protected UdpServer(IUdpServerChannelFactory serverChannelFactory, ILogger logger, IUdpServerEventLoopGroupFactory udpServerEventLoopGroupFactory) : base(serverChannelFactory, logger, udpServerEventLoopGroupFactory) { } diff --git a/src/Catalyst.Core.Lib/P2P/IO/Messaging/Broadcast/BroadcastManager.cs b/src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Messaging/Broadcast/BroadcastManager.cs similarity index 84% rename from src/Catalyst.Core.Lib/P2P/IO/Messaging/Broadcast/BroadcastManager.cs rename to src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Messaging/Broadcast/BroadcastManager.cs index 5664243efe..7ae1a46c54 100644 --- a/src/Catalyst.Core.Lib/P2P/IO/Messaging/Broadcast/BroadcastManager.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Messaging/Broadcast/BroadcastManager.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,23 +30,23 @@ using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Extensions.Protocol.Wire; -using Catalyst.Core.Lib.IO.Messaging.Dto; -using Catalyst.Core.Lib.P2P.Repository; using Catalyst.Protocol.Cryptography; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Google.Protobuf; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Primitives; using Serilog; +using MultiFormats; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.Abstractions; -namespace Catalyst.Core.Lib.P2P.IO.Messaging.Broadcast +namespace Catalyst.Modules.Network.Dotnetty.P2P.IO.Messaging.Broadcast { /// - /// The Gossip Manager used to broadcast and receive gossip messages + /// The Gossip Manager used to broadcast and receive gossip messages /// /// public sealed class BroadcastManager : IBroadcastManager @@ -63,10 +63,10 @@ public sealed class BroadcastManager : IBroadcastManager private readonly Func _entryOptions; /// The peer identifier - private readonly PeerId _peerId; + private readonly MultiAddress _peerId; /// The peer client - private readonly IPeerClient _peerClient; + private readonly IDotnettyUdpClient _dotnettyUdpClient; /// This signer is in-charge of adding an extra signature wrapping to the broadcast message private readonly IKeySigner _signer; @@ -82,30 +82,35 @@ public sealed class BroadcastManager : IBroadcastManager /// The maximum peers a broadcast originator can gossip to for a single message, per gossip cycle. public static int BroadcastOwnerMaximumGossipPeersPerRound => 10; - /// Initializes a new instance of the class. + /// Initializes a new instance of the class. /// The peers. /// Peer settings /// The memory cache. /// The peer client. /// The signature writer /// - public BroadcastManager(IPeerRepository peers, + public BroadcastManager(IPeerRepository peers, IPeerSettings peerSettings, IMemoryCache memoryCache, - IPeerClient peerClient, + IDotnettyUdpClient dotnettyUdpClient, IKeySigner signer, ILogger logger) { _logger = logger; - _peerId = peerSettings.PeerId; + _peerId = peerSettings.Address; _pendingRequests = memoryCache; _peers = peers; - _signingContext = new SigningContext {NetworkType = peerSettings.NetworkType, SignatureType = SignatureType.ProtocolPeer}; - _peerClient = peerClient; + _signingContext = new SigningContext + { + NetworkType = peerSettings.NetworkType, + SignatureType = SignatureType.ProtocolPeer + }; + _dotnettyUdpClient = dotnettyUdpClient; _signer = signer; _incomingBroadcastSignatureDictionary = new ConcurrentDictionary(); _entryOptions = () => new MemoryCacheEntryOptions() - .AddExpirationToken(new CancellationChangeToken(new CancellationTokenSource(TimeSpan.FromMinutes(10)).Token)); + .AddExpirationToken( + new CancellationChangeToken(new CancellationTokenSource(TimeSpan.FromMinutes(10)).Token)); } private async Task BroadcastInnerAsync(ProtocolMessage signedMessage) @@ -128,11 +133,11 @@ private async Task BroadcastInnerAsync(ProtocolMessage signedMessage) SendBroadcastMessages(signedMessage, gossipRequest); } - /// + /// public async Task BroadcastAsync(ProtocolMessage message) { var correlationId = message.CorrelationId.ToCorrelationId(); - bool containsOriginalMessage = + var containsOriginalMessage = _incomingBroadcastSignatureDictionary.ContainsKey(correlationId); if (containsOriginalMessage) @@ -148,13 +153,14 @@ public async Task BroadcastAsync(ProtocolMessage message) } } - /// + /// public async Task ReceiveAsync(ProtocolMessage protocolSignedMessage) { var correlationId = protocolSignedMessage.CorrelationId.ToCorrelationId(); var gossipRequest = await GetOrCreateAsync(correlationId).ConfigureAwait(false); gossipRequest.IncrementReceivedCount(); - _logger.Verbose("Received broadcast message {message} {gossipCount} times.", correlationId, gossipRequest.ReceivedCount); + _logger.Verbose("Received broadcast message {message} {gossipCount} times.", correlationId, + gossipRequest.ReceivedCount); UpdatePendingRequest(correlationId, gossipRequest); _incomingBroadcastSignatureDictionary.GetOrAdd(correlationId, protocolSignedMessage); } @@ -170,7 +176,7 @@ private void SendBroadcastMessages(ProtocolMessage message, BroadcastMessage bro try { var innerMessage = message.FromProtocolMessage(); - var isOwnerOfBroadcast = innerMessage.PeerId.Equals(_peerId); + var isOwnerOfBroadcast = innerMessage.Address == _peerId.ToString(); if (isOwnerOfBroadcast) { @@ -184,6 +190,7 @@ private void SendBroadcastMessages(ProtocolMessage message, BroadcastMessage bro : (int) Math.Max(GetMaxGossipCycles(broadcastMessage), MaxGossipPeersPerRound); var peersToGossip = GetRandomPeers(fanOut); + var correlationId = innerMessage.CorrelationId.ToCorrelationId(); //CLEAN UP @@ -191,11 +198,8 @@ private void SendBroadcastMessages(ProtocolMessage message, BroadcastMessage bro { _logger.Verbose("Broadcasting message {message}", message); var protocolMessage = message.Clone(); - protocolMessage.PeerId = _peerId; - _peerClient.SendMessage(new MessageDto( - protocolMessage, - peerIdentifier) - ); + protocolMessage.Address = _peerId.ToString(); + _dotnettyUdpClient.SendMessageAsync(protocolMessage, peerIdentifier); } var updateCount = (uint) peersToGossip.Count; @@ -216,10 +220,9 @@ private void SendBroadcastMessages(ProtocolMessage message, BroadcastMessage bro /// Gets the random peers. /// The count. /// - private List GetRandomPeers(int count) + private List GetRandomPeers(int count) { - return _peers - .AsQueryable().Select(c => c.DocumentId).Shuffle().Take(count).Select(_peers.Get).Select(p => p.PeerId).ToList(); + return _peers.GetRandomPeers(count).Select(p => p.Address).ToList(); } /// Determines whether this instance can gossip the specified correlation identifier. @@ -241,7 +244,7 @@ private void UpdatePendingRequest(ICorrelationId correlationId, BroadcastMessage /// Gets the maximum gossip cycles. /// /// - private uint GetMaxGossipCycles(BroadcastMessage broadcastMessage) + private static uint GetMaxGossipCycles(BroadcastMessage broadcastMessage) { var peerNetworkSize = broadcastMessage.PeerNetworkSize; return (uint) (Math.Log(Math.Max(10, peerNetworkSize) / (double) MaxGossipPeersPerRound) / @@ -259,7 +262,7 @@ private async Task GetOrCreateAsync(ICorrelationId correlation { ReceivedCount = 0, BroadcastCount = 0, - PeerNetworkSize = _peers.AsQueryable().Count() + PeerNetworkSize = _peers.Count() }).ConfigureAwait(false); entry.Value = gossipRequest; return gossipRequest; diff --git a/src/Catalyst.Core.Lib/P2P/IO/Messaging/Broadcast/BroadcastMessage.cs b/src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Messaging/Broadcast/BroadcastMessage.cs similarity index 94% rename from src/Catalyst.Core.Lib/P2P/IO/Messaging/Broadcast/BroadcastMessage.cs rename to src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Messaging/Broadcast/BroadcastMessage.cs index f3b486a48a..3f7ed4b582 100644 --- a/src/Catalyst.Core.Lib/P2P/IO/Messaging/Broadcast/BroadcastMessage.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Messaging/Broadcast/BroadcastMessage.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,7 @@ using System.Threading; -namespace Catalyst.Core.Lib.P2P.IO.Messaging.Broadcast +namespace Catalyst.Modules.Network.Dotnetty.P2P.IO.Messaging.Broadcast { /// /// Represents a gossip request to the gossip cache diff --git a/src/Catalyst.Core.Lib/P2P/IO/Transport/Channels/PeerClientChannelFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Transport/Channels/PeerClientChannelFactory.cs similarity index 82% rename from src/Catalyst.Core.Lib/P2P/IO/Transport/Channels/PeerClientChannelFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Transport/Channels/PeerClientChannelFactory.cs index e9a7121d54..9d19c40a96 100644 --- a/src/Catalyst.Core.Lib/P2P/IO/Transport/Channels/PeerClientChannelFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Transport/Channels/PeerClientChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,19 +23,18 @@ using System; using System.Collections.Generic; -using System.Net; using System.Reactive.Concurrency; using System.Reactive.Linq; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Handlers; -using Catalyst.Core.Lib.IO.Transport.Channels; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Wire; using DotNetty.Codecs; @@ -43,10 +42,11 @@ using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Sockets; using Google.Protobuf; +using MultiFormats; -namespace Catalyst.Core.Lib.P2P.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.P2P.IO.Transport.Channels { - public class PeerClientChannelFactory : UdpClientChannelFactory + public class PeerClientChannelFactory : UdpClientChannelFactory { private readonly IScheduler _scheduler; private readonly IKeySigner _keySigner; @@ -96,13 +96,12 @@ public PeerClientChannelFactory(IKeySigner keySigner, /// Ignored /// Ignored /// Local TLS certificate - public override async Task BuildChannelAsync(IEventLoopGroupFactory handlerEventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + public override async Task> BuildChannelAsync(IEventLoopGroupFactory handlerEventLoopGroupFactory, + MultiAddress address, X509Certificate2 certificate = null) { - var channel = await BootStrapChannelAsync(handlerEventLoopGroupFactory, targetAddress, targetPort).ConfigureAwait(false); - return new ObservableChannel(Observable.Never>(), channel); + var channel = await BootStrapChannelAsync(handlerEventLoopGroupFactory, address.GetIpAddress(), address.GetPort()).ConfigureAwait(false); + return new ObservableChannel(Observable.Never(), channel); } } } diff --git a/src/Catalyst.Core.Lib/P2P/IO/Transport/Channels/PeerServerChannelFactory.cs b/src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Transport/Channels/PeerServerChannelFactory.cs similarity index 78% rename from src/Catalyst.Core.Lib/P2P/IO/Transport/Channels/PeerServerChannelFactory.cs rename to src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Transport/Channels/PeerServerChannelFactory.cs index 1ed610659d..8d7ba99730 100644 --- a/src/Catalyst.Core.Lib/P2P/IO/Transport/Channels/PeerServerChannelFactory.cs +++ b/src/Catalyst.Modules.Network.Dotnetty/P2P/IO/Transport/Channels/PeerServerChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,31 +23,32 @@ using System; using System.Collections.Generic; -using System.Net; using System.Reactive.Concurrency; using System.Reactive.Linq; +using System.Reactive.Subjects; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; using Catalyst.Abstractions.IO.Handlers; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Transport.Channels; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.P2P.IO.Messaging.Broadcast; using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Handlers; -using Catalyst.Core.Lib.IO.Transport.Channels; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.P2P.IO.Messaging.Broadcast; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Wire; using DotNetty.Codecs; using DotNetty.Codecs.Protobuf; using DotNetty.Transport.Channels; using Google.Protobuf; +using MultiFormats; -namespace Catalyst.Core.Lib.P2P.IO.Transport.Channels +namespace Catalyst.Modules.Network.Dotnetty.P2P.IO.Transport.Channels { - public class PeerServerChannelFactory : UdpServerChannelFactory + public class PeerServerChannelFactory : UdpServerChannelFactory { private readonly IScheduler _scheduler; private readonly IPeerMessageCorrelationManager _messageCorrelationManager; @@ -55,6 +56,7 @@ public class PeerServerChannelFactory : UdpServerChannelFactory private readonly IKeySigner _keySigner; private readonly IPeerIdValidator _peerIdValidator; private readonly SigningContext _signingContext; + public IObservable MessageStream { get; } public PeerServerChannelFactory(IPeerMessageCorrelationManager messageCorrelationManager, IBroadcastManager broadcastManager, @@ -69,6 +71,9 @@ public PeerServerChannelFactory(IPeerMessageCorrelationManager messageCorrelatio _keySigner = keySigner; _peerIdValidator = peerIdValidator; _signingContext = new SigningContext {NetworkType = peerSettings.NetworkType, SignatureType = SignatureType.ProtocolPeer}; + + ReplaySubject messageSubject = new(); + MessageStream = messageSubject.AsObservable(); } protected override Func> HandlerGenerationFunction @@ -105,17 +110,15 @@ protected override Func> HandlerGenerationFunction /// Ignored /// Ignored /// - public override async Task BuildChannelAsync(IEventLoopGroupFactory handlerEventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + public override async Task> BuildChannelAsync(IEventLoopGroupFactory handlerEventLoopGroupFactory, + MultiAddress address, X509Certificate2 certificate = null) { - var channel = await BootStrapChannelAsync(handlerEventLoopGroupFactory, targetAddress, targetPort).ConfigureAwait(false); + var channel = await BootStrapChannelAsync(handlerEventLoopGroupFactory, address.GetIpAddress(), address.GetPort()).ConfigureAwait(false); - var messageStream = channel.Pipeline.Get()?.MessageStream; + var messageStream = channel.Pipeline.Get>()?.MessageStream; - return new ObservableChannel(messageStream - ?? Observable.Never>(), channel); + return new ObservableChannel(messageStream ?? Observable.Never(), channel); } } } diff --git a/src/Catalyst.Modules.Network.Dotnetty/Util/NullObjects.cs b/src/Catalyst.Modules.Network.Dotnetty/Util/NullObjects.cs new file mode 100644 index 0000000000..1cc11fb176 --- /dev/null +++ b/src/Catalyst.Modules.Network.Dotnetty/Util/NullObjects.cs @@ -0,0 +1,33 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Protocol.Wire; + +namespace Catalyst.Modules.Network.Dotnetty.Util +{ + public static class NullObjects + { + public static readonly ObserverDto ObserverDto = new(null, new ProtocolMessage()); + } +} diff --git a/src/Catalyst.Modules.Network.LibP2P/Catalyst.Modules.Network.LibP2P.csproj b/src/Catalyst.Modules.Network.LibP2P/Catalyst.Modules.Network.LibP2P.csproj new file mode 100644 index 0000000000..9de3050f14 --- /dev/null +++ b/src/Catalyst.Modules.Network.LibP2P/Catalyst.Modules.Network.LibP2P.csproj @@ -0,0 +1,16 @@ + + + + net6.0 + + + + + + + + + + + + diff --git a/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Inbound/CorrelationHandler.cs b/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Inbound/CorrelationHandler.cs new file mode 100644 index 0000000000..7e422d204e --- /dev/null +++ b/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Inbound/CorrelationHandler.cs @@ -0,0 +1,67 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Reflection; +using System.Threading.Tasks; +using Catalyst.Abstractions.IO.Handlers; +using Catalyst.Abstractions.IO.Messaging.Correlation; +using Catalyst.Abstractions.Types; +using Catalyst.Protocol.Wire; +using Serilog; + +namespace Catalyst.Core.Lib.IO.Handlers +{ + public sealed class CorrelationHandler : IInboundMessageHandler where T : IMessageCorrelationManager + { + private static readonly ILogger Logger = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType); + private readonly T _messageCorrelationManager; + + public CorrelationHandler(T messageCorrelationManager) + { + _messageCorrelationManager = messageCorrelationManager; + } + + /// + /// The server should always correlate a response, if it can fire next pipeline, if not close the channel, + /// If the message is not a response (IE Request/Broadcast) it should pass on to the next handler without attempting to correlate. + /// + /// + public Task ProcessAsync(ProtocolMessage message) + { + Logger.Verbose("Received {message}", message); + if (message.TypeUrl.EndsWith(MessageTypes.Response.Name)) + { + if (_messageCorrelationManager.TryMatchResponse(message)) + { + return Task.FromResult(true); + } + } + else + { + return Task.FromResult(true); + } + + return Task.FromResult(false); + } + } +} diff --git a/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Inbound/PeerIdValidationHandler.cs b/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Inbound/PeerIdValidationHandler.cs new file mode 100644 index 0000000000..6af5d9560a --- /dev/null +++ b/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Inbound/PeerIdValidationHandler.cs @@ -0,0 +1,51 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Reflection; +using System.Threading.Tasks; +using Catalyst.Abstractions.IO.Handlers; +using Catalyst.Abstractions.P2P; +using Catalyst.Protocol.Wire; +using Serilog; + +namespace Catalyst.Core.Lib.IO.Handlers +{ + public sealed class PeerIdValidationHandler : IInboundMessageHandler + { + private static readonly ILogger Logger = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IPeerIdValidator _peerIdValidator; + public PeerIdValidationHandler(IPeerIdValidator peerIdValidator) { _peerIdValidator = peerIdValidator; } + + public Task ProcessAsync(ProtocolMessage message) + { + Logger.Verbose("Received {msg}", message); + if (_peerIdValidator.ValidatePeerIdFormat(message.Address)) + { + return Task.FromResult(true); + } + + return Task.FromResult(false); + } + } +} diff --git a/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Inbound/ProtocolMessageVerifyHandler.cs b/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Inbound/ProtocolMessageVerifyHandler.cs new file mode 100644 index 0000000000..e046cdeb05 --- /dev/null +++ b/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Inbound/ProtocolMessageVerifyHandler.cs @@ -0,0 +1,87 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Reflection; +using System.Threading.Tasks; +using Catalyst.Abstractions.IO.Handlers; +using Catalyst.Abstractions.KeySigner; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Protocol.Wire; +using MultiFormats; +using Serilog; + +namespace Catalyst.Core.Lib.IO.Handlers +{ + public sealed class ProtocolMessageVerifyHandler : IInboundMessageHandler + { + private static readonly ILogger Logger = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType); + private readonly IKeySigner _keySigner; + + public ProtocolMessageVerifyHandler(IKeySigner keySigner) + { + _keySigner = keySigner; + } + + public Task ProcessAsync(ProtocolMessage message) + { + Logger.Verbose("Received {msg}", message); + if (!Verify(message)) + { + Logger.Warning("Failed to verify {msg} signature.", message); + return Task.FromResult(false); + } + + if (message.IsBroadCastMessage()) + { + var innerSignedMessage = ProtocolMessage.Parser.ParseFrom(message.Value); + if (!Verify(innerSignedMessage)) + { + Logger.Warning("Failed to verify inner signature in broadcast message {msg}.", innerSignedMessage); + return Task.FromResult(false); + } + } + + return Task.FromResult(true); + } + + private bool Verify(ProtocolMessage signedMessage) + { + if (signedMessage.Signature == null) + { + return false; + } + + var sig = signedMessage.Signature.RawBytes.ToByteArray(); + MultiAddress address = new(signedMessage.Address); + var pub = address.GetPublicKeyBytes(); + + var signature = _keySigner.CryptoContext.GetSignatureFromBytes(sig, pub); + var messageWithoutSig = signedMessage.Clone(); + messageWithoutSig.Signature = null; + + var verified = _keySigner.Verify(signature, messageWithoutSig, signedMessage.Signature.SigningContext); + + return verified; + } + } +} diff --git a/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Outbound/CorrelatableHandler.cs b/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Outbound/CorrelatableHandler.cs new file mode 100644 index 0000000000..4aafe73094 --- /dev/null +++ b/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Outbound/CorrelatableHandler.cs @@ -0,0 +1,59 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading.Tasks; +using Catalyst.Abstractions.IO.Handlers; +using Catalyst.Abstractions.IO.Messaging.Correlation; +using Catalyst.Abstractions.Types; +using Catalyst.Core.Lib.IO.Messaging.Correlation; +using Catalyst.Protocol.Wire; + +namespace Catalyst.Core.Lib.IO.Handlers +{ + public sealed class CorrelatableHandler : IOutboundMessageHandler where T : IMessageCorrelationManager + { + private readonly T _messageCorrelationManager; + + /// + public CorrelatableHandler(T messageCorrelationManager) + { + _messageCorrelationManager = messageCorrelationManager; + } + + public Task ProcessAsync(ProtocolMessage message) + { + if (message.TypeUrl.EndsWith(MessageTypes.Request.Name)) + { + _messageCorrelationManager.AddPendingRequest(new CorrelatableMessage + { + Recipient = message.Address, + Content = message, + SentAt = DateTimeOffset.UtcNow + }); + } + + return Task.FromResult(true); + } + } +} diff --git a/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Outbound/ProtocolMessageSignHandler.cs b/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Outbound/ProtocolMessageSignHandler.cs new file mode 100644 index 0000000000..5e1501c633 --- /dev/null +++ b/src/Catalyst.Modules.Network.LibP2P/IO/Handlers/Outbound/ProtocolMessageSignHandler.cs @@ -0,0 +1,61 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Reflection; +using System.Threading.Tasks; +using Catalyst.Abstractions.IO.Handlers; +using Catalyst.Abstractions.KeySigner; +using Catalyst.Core.Lib.Extensions.Protocol.Wire; +using Catalyst.Protocol.Cryptography; +using Catalyst.Protocol.Wire; +using Serilog; + +namespace Catalyst.Core.Lib.IO.Handlers +{ + public sealed class ProtocolMessageSignHandler : IOutboundMessageHandler + { + private static readonly ILogger Logger = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType); + + private readonly IKeySigner _keySigner; + private readonly SigningContext _signingContext; + + public ProtocolMessageSignHandler(IKeySigner keySigner, SigningContext signingContext) + { + _keySigner = keySigner; + _signingContext = signingContext; + } + + /// + /// Signs a protocol message, or straight WriteAndFlush non-protocolMessages + /// + /// + /// + /// + public Task ProcessAsync(ProtocolMessage message) + { + Logger.Verbose("Signing message {message}", message); + message.Sign(_keySigner, _signingContext); + return Task.FromResult(true); + } + } +} diff --git a/src/Catalyst.Modules.Network.LibP2P/LibP2PNetworkModule.cs b/src/Catalyst.Modules.Network.LibP2P/LibP2PNetworkModule.cs new file mode 100644 index 0000000000..4abe12684e --- /dev/null +++ b/src/Catalyst.Modules.Network.LibP2P/LibP2PNetworkModule.cs @@ -0,0 +1,42 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Autofac; +using Catalyst.Abstractions.P2P; +using Catalyst.Core.Lib.P2P; +using Lib.P2P.Protocols; + +namespace Catalyst.Modules.Network.LibP2P +{ + public class LibP2PNetworkModule : Module + { + protected override void Load(ContainerBuilder builder) + { + builder.RegisterType().AsImplementedInterfaces().SingleInstance(); + + // Register P2P + builder.RegisterType().As().SingleInstance(); + builder.RegisterType().As().SingleInstance(); + } + } +} diff --git a/src/Catalyst.Modules.Network.LibP2P/LibP2PPeerClient.cs b/src/Catalyst.Modules.Network.LibP2P/LibP2PPeerClient.cs new file mode 100644 index 0000000000..05b163c7b1 --- /dev/null +++ b/src/Catalyst.Modules.Network.LibP2P/LibP2PPeerClient.cs @@ -0,0 +1,166 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.IO.Handlers; +using Catalyst.Abstractions.KeySigner; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.IO.Handlers; +using Catalyst.Protocol.Cryptography; +using Catalyst.Protocol.Wire; +using Google.Protobuf; +using Lib.P2P.Protocols; +using MultiFormats; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Catalyst.Core.Lib.P2P +{ + public class LibP2PPeerClient : IPeerClient + { + private bool _disposed; + + private readonly IPeerSettings _peerSettings; + private readonly IPubSubApi _pubSubApi; + private readonly ICatalystProtocol _catalystProtocol; + private readonly SigningContext _signingContext; + private IList _catalystProtocolHandlers; + private IList _catalystPubSubHandlers; + private readonly IKeySigner _keySigner; + private readonly IPeerMessageCorrelationManager _messageCorrelationManager; + + public IObservable MessageStream { private set; get; } + + /// A factory used to build the appropriate kind of channel for a udp client. + /// + /// + public LibP2PPeerClient( + IPeerMessageCorrelationManager messageCorrelationManager, + IKeySigner keySigner, + IPeerSettings peerSettings, + IPubSubApi pubSubApi, + ICatalystProtocol catalystProtocol) + { + _messageCorrelationManager = messageCorrelationManager; + _keySigner = keySigner; + _peerSettings = peerSettings; + _pubSubApi = pubSubApi; + _catalystProtocol = catalystProtocol; + _signingContext = new SigningContext { NetworkType = peerSettings.NetworkType, SignatureType = SignatureType.ProtocolPeer }; + } + + public async Task StartAsync() + { + await StartAsync(CancellationToken.None).ConfigureAwait(false); + } + + public Task StartAsync(CancellationToken cancellationToken) + { + _catalystProtocolHandlers = new List + { + new ProtocolMessageSignHandler(_keySigner, _signingContext), + new CorrelatableHandler(_messageCorrelationManager) + }; + + _catalystPubSubHandlers = new List + { + new ProtocolMessageSignHandler(_keySigner, _signingContext) + }; + + return Task.CompletedTask; + } + + public async Task SendMessageToPeersAsync(IMessage message, IEnumerable peers) + { + var protocolMessage = message.ToProtocolMessage(_peerSettings.Address); + foreach (var peer in peers) + { + await SendMessageAsync(protocolMessage, peer).ConfigureAwait(false); + } + } + + public async Task SendMessageAsync(ProtocolMessage message, MultiAddress receiver) + { + if (!await ProcessHandlersAsync(_catalystProtocolHandlers, message).ConfigureAwait(false)) + { + return; + } + + try + { + await _catalystProtocol.SendAsync(receiver, message).ConfigureAwait(false); + } + catch (Exception exc) + { + //Peer does not support catalyst protocol + } + } + + public async Task BroadcastAsync(ProtocolMessage message) + { + if (!await ProcessHandlersAsync(_catalystPubSubHandlers, message).ConfigureAwait(false)) + { + return; + } + + await _pubSubApi.PublishAsync("catalyst", message.ToByteArray()).ConfigureAwait(false); + } + + private static async Task ProcessHandlersAsync(IList handlers, ProtocolMessage message) + { + foreach (var handler in handlers) + { + var result = await handler.ProcessAsync(message).ConfigureAwait(false); + if (!result) + { + return false; + } + } + + return true; + } + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + //dispose disposables + } + _disposed = true; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} + diff --git a/src/Catalyst.Modules.Network.LibP2P/LibP2PPeerService.cs b/src/Catalyst.Modules.Network.LibP2P/LibP2PPeerService.cs new file mode 100644 index 0000000000..57e5364ad7 --- /dev/null +++ b/src/Catalyst.Modules.Network.LibP2P/LibP2PPeerService.cs @@ -0,0 +1,191 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Catalyst.Abstractions.Dfs.CoreApi; +using Catalyst.Abstractions.IO.Handlers; +using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.KeySigner; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.IO.Messaging.Correlation; +using Catalyst.Core.Lib.IO.Handlers; +using Catalyst.Protocol.Wire; +using Lib.P2P; +using Lib.P2P.Protocols; +using Lib.P2P.PubSub; +using Serilog; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reactive.Concurrency; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Threading; +using System.Threading.Tasks; + +namespace Catalyst.Core.Lib.P2P +{ + public class LibP2PPeerService : IPeerService + { + private bool _disposed; + + private readonly Peer _localPeer; + private readonly IPubSubApi _pubSubApi; + private readonly ICatalystProtocol _catalystProtocol; + private readonly IList _handlers; + private readonly IEnumerable _messageObservers; + private readonly IPubSubService _pubSubService; + + private readonly ReplaySubject _messageSubject; + public IObservable MessageStream { private set; get; } + + /// A factory used to build the appropriate kind of channel for a udp client. + /// + /// + public LibP2PPeerService( + IEnumerable messageObservers, + IPeerMessageCorrelationManager messageCorrelationManager, + IKeySigner keySigner, + IPeerIdValidator peerIdValidator, + Peer localPeer, + IPubSubApi pubSubApi, + ICatalystProtocol catalystProtocol, + IPubSubService pubSubService, + ILogger logger + ) : this(messageObservers, messageCorrelationManager, keySigner, peerIdValidator, localPeer, pubSubApi, catalystProtocol, pubSubService, logger, Scheduler.Default) + { } + + public LibP2PPeerService( + IEnumerable messageObservers, + IPeerMessageCorrelationManager messageCorrelationManager, + IKeySigner keySigner, + IPeerIdValidator peerIdValidator, + Peer localPeer, + IPubSubApi pubSubApi, + ICatalystProtocol catalystProtocol, + IPubSubService pubSubService, + ILogger logger, + IScheduler scheduler) + { + _localPeer = localPeer; + _pubSubApi = pubSubApi; + _catalystProtocol = catalystProtocol; + _pubSubService = pubSubService; + _messageSubject = new ReplaySubject(scheduler); + MessageStream = _messageSubject.AsObservable(); + + _handlers = new List + { + new PeerIdValidationHandler(peerIdValidator), + new ProtocolMessageVerifyHandler(keySigner), + new CorrelationHandler(messageCorrelationManager) + }; + + _messageObservers = messageObservers; + } + + + /// + /// Ignored + /// Ignored + /// Ignored + /// + public async Task> BuildMessageStreamAsync() + { + await SubscribeToCatalystLibP2PProtocol().ConfigureAwait(false); + + await SubscribeToCatalystPubSub().ConfigureAwait(false); + + return MessageStream; + } + + private Task SubscribeToCatalystLibP2PProtocol() + { + _catalystProtocol.MessageStream.Subscribe(async message => + { + await ProcessMessageAsync(message).ConfigureAwait(false); + }); + + return Task.CompletedTask; + } + + private async Task SubscribeToCatalystPubSub() + { + await _pubSubApi.SubscribeAsync("catalyst", async msg => + { + if (msg.Sender.Id != _localPeer.Id) + { + var protocolMessage = ProtocolMessage.Parser.ParseFrom(msg.DataStream); + await ProcessMessageAsync(protocolMessage).ConfigureAwait(false); + } + }, CancellationToken.None).ConfigureAwait(false); + } + + private async Task ProcessMessageAsync(ProtocolMessage message) + { + foreach (var handler in _handlers) + { + var result = await handler.ProcessAsync(message).ConfigureAwait(false); + if (!result) + { + return; + } + } + + _messageSubject.OnNext(message); + } + + public async Task StartAsync() + { + await StartAsync(CancellationToken.None).ConfigureAwait(false); + } + + public async Task StartAsync(CancellationToken cancellationToken) + { + MessageStream = await BuildMessageStreamAsync().ConfigureAwait(false); + _messageObservers.ToList().ForEach(h => h.StartObserving(MessageStream)); + + foreach (var router in _pubSubService.Routers) + { + await router.JoinTopicAsync("catalyst", cancellationToken).ConfigureAwait(false); + } + } + + private void Dispose(bool disposing) + { + if (!_disposed) + { + if (disposing) + { + _messageSubject.Dispose(); + } + _disposed = true; + } + } + + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + } +} diff --git a/src/Catalyst.Modules.Network.LibP2P/Protocols/CatalystProtocol.cs b/src/Catalyst.Modules.Network.LibP2P/Protocols/CatalystProtocol.cs new file mode 100644 index 0000000000..8d1a8147bd --- /dev/null +++ b/src/Catalyst.Modules.Network.LibP2P/Protocols/CatalystProtocol.cs @@ -0,0 +1,190 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Reactive.Linq; +using System.Reactive.Subjects; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Protocol.Wire; +using Common.Logging; +using Google.Protobuf; +using MultiFormats; +using ProtoBuf; +using Semver; + +namespace Lib.P2P.Protocols +{ + /// + /// Catalyst Protocol version 1.0 + /// + public class CatalystProtocol : ICatalystProtocol, IPeerProtocol, IService + { + private static ILog _log = LogManager.GetLogger(typeof(CatalystProtocol)); + + /// + public string Name { get; } = "ipfs/catalyst"; + + /// + public SemVersion Version { get; } = new(1); + + /// + /// Provides access to other peers. + /// + public ISwarmService SwarmService { get; set; } + + /// + public override string ToString() { return $"/{Name}/{Version}"; } + + public ReplaySubject ResponseMessageSubject { get; } + public IObservable MessageStream { get; } + + /// + /// + /// + /// + public CatalystProtocol(ISwarmService swarmService) + { + SwarmService = swarmService; + ResponseMessageSubject = new ReplaySubject(1); + MessageStream = ResponseMessageSubject.AsObservable(); + } + + /// + public async Task ProcessMessageAsync(PeerConnection connection, + Stream stream, + CancellationToken cancel = default) + { + try + { + var request = await ProtoBufHelper.ReadMessageAsync(stream, cancel).ConfigureAwait(false); + var protocolMessage = ProtocolMessage.Parser.ParseFrom(request.Message); + ResponseMessageSubject.OnNext(protocolMessage); + } + catch (Exception e) + { + _log.Warn("Receiving CatalystProtocol", e); + } + } + + /// + public Task StartAsync() + { + _log.Debug("Starting"); + + SwarmService.AddProtocol(this); + + return Task.CompletedTask; + } + + /// + public Task StopAsync() + { + _log.Debug("Stopping"); + + SwarmService.RemoveProtocol(this); + + return Task.CompletedTask; + } + + /// + /// Send a catalyst message request to a peer. + /// + /// + /// The peer ID to receive the message requests. + /// + /// + /// The message request to send. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + public async Task SendAsync(MultiHash peerId, + ProtocolMessage message) + { + await SendAsync(peerId, message, CancellationToken.None).ConfigureAwait(false); + } + + public async Task SendAsync(MultiHash peerId, + ProtocolMessage message, + CancellationToken cancel) + { + var peer = new Peer { Id = peerId }; + await SendAsync(peer, message, cancel).ConfigureAwait(false); + } + + /// + /// Send a catalyst message request to a peer. + /// + /// + /// The address of a peer to receive the echo requests. + /// + /// + /// The message request to send. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + public async Task SendAsync(MultiAddress address, + ProtocolMessage message) + { + await SendAsync(address, message, CancellationToken.None).ConfigureAwait(false); + } + + public async Task SendAsync(MultiAddress address, + ProtocolMessage message, + CancellationToken cancel) + { + var peer = SwarmService.RegisterPeerAddress(address); + await SendAsync(peer, message, cancel).ConfigureAwait(false); + } + + private async Task SendAsync(Peer peer, ProtocolMessage message, CancellationToken cancel) + { + await using (var stream = await SwarmService.DialAsync(peer, ToString(), cancel)) + { + var catalystByteMessage = new CatalystByteMessage + { + Message = message.ToByteArray() + }; + + Serializer.SerializeWithLengthPrefix(stream, catalystByteMessage, PrefixStyle.Base128); + await stream.FlushAsync(cancel).ConfigureAwait(false); + } + } + + [ProtoContract] + public sealed class CatalystByteMessage + { + [ProtoMember(1)] + public byte[] Message { set; get; } + } + } +} diff --git a/src/Catalyst.Modules.Network.LibP2P/Protocols/ICatalystProtocol.cs b/src/Catalyst.Modules.Network.LibP2P/Protocols/ICatalystProtocol.cs new file mode 100644 index 0000000000..cd69598fa2 --- /dev/null +++ b/src/Catalyst.Modules.Network.LibP2P/Protocols/ICatalystProtocol.cs @@ -0,0 +1,49 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading; +using System.Threading.Tasks; +using Catalyst.Protocol.Wire; +using MultiFormats; + +namespace Lib.P2P.Protocols +{ + public interface ICatalystProtocol + { + IObservable MessageStream { get; } + + Task SendAsync(MultiHash peerId, + ProtocolMessage message); + Task SendAsync(MultiHash peerId, + ProtocolMessage message, + CancellationToken cancel); + + Task SendAsync(MultiAddress address, + ProtocolMessage message); + + Task SendAsync(MultiAddress address, + ProtocolMessage message, + CancellationToken cancel); + } +} diff --git a/src/Catalyst.Modules.POA.Consensus.Tests/Catalyst.Modules.POA.Consensus.Tests.csproj b/src/Catalyst.Modules.POA.Consensus.Tests/Catalyst.Modules.POA.Consensus.Tests.csproj index e611a874e3..87ff88866f 100644 --- a/src/Catalyst.Modules.POA.Consensus.Tests/Catalyst.Modules.POA.Consensus.Tests.csproj +++ b/src/Catalyst.Modules.POA.Consensus.Tests/Catalyst.Modules.POA.Consensus.Tests.csproj @@ -1,8 +1,8 @@ - + - netcoreapp3.0 + net6.0 Catalyst POA Consensus Module Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Modules.POA.Consensus.Tests.snk true @@ -10,6 +10,9 @@ Catalyst.Modules.POA.Consensus.Tests + + 1701;1702;VSTHRD200;CS8002 + diff --git a/src/Catalyst.Modules.POA.Consensus.Tests/UnitTests/Deltas/PoaDeltaProducersProviderTests.cs b/src/Catalyst.Modules.POA.Consensus.Tests/UnitTests/Deltas/PoaDeltaProducersProviderTests.cs index 5b83d0f376..816cb990dc 100644 --- a/src/Catalyst.Modules.POA.Consensus.Tests/UnitTests/Deltas/PoaDeltaProducersProviderTests.cs +++ b/src/Catalyst.Modules.POA.Consensus.Tests/UnitTests/Deltas/PoaDeltaProducersProviderTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,84 +25,98 @@ using System.Collections.Generic; using System.Linq; using Catalyst.Abstractions.Hashing; -using Catalyst.Core.Lib.P2P.Repository; using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; using Catalyst.Core.Modules.Hashing; using Catalyst.Modules.POA.Consensus.Deltas; -using Catalyst.Protocol.Peer; using Catalyst.TestUtils; using FluentAssertions; -using Google.Protobuf; -using LibP2P; +using Lib.P2P; using Microsoft.Extensions.Caching.Memory; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; +using NUnit.Framework; using Peer = Catalyst.Core.Lib.P2P.Models.Peer; +using Catalyst.Core.Lib.Extensions; +using Nethermind.Core; +using Catalyst.Abstractions.Validators; +using Catalyst.Core.Modules.Kvm; +using Catalyst.Abstractions.Kvm; +using Catalyst.Abstractions.Consensus.Deltas; +using Catalyst.Protocol.Deltas; namespace Catalyst.Modules.POA.Consensus.Tests.UnitTests.Deltas { public class PoaDeltaProducersProviderTests { - private readonly Peer _selfAsPeer; - private readonly List _peers; - private readonly PoaDeltaProducersProvider _poaDeltaProducerProvider; - private readonly Cid _previousDeltaHash; - private readonly IMemoryCache _producersByPreviousDelta; - private readonly IHashProvider _hashProvider; - - public PoaDeltaProducersProviderTests() + private List _peers; + private PoaDeltaProducersProvider _poaDeltaProducerProvider; + private Cid _previousDeltaHash; + private IMemoryCache _producersByPreviousDelta; + private IHashProvider _hashProvider; + + [SetUp] + public void Init() { - _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); - var peerSettings = PeerIdHelper.GetPeerId("TEST").ToSubstitutedPeerSettings(); - _selfAsPeer = new Peer {PeerId = peerSettings.PeerId}; - var rand = new Random(); + var peerSettings = MultiAddressHelper.GetAddress("TEST").ToSubstitutedPeerSettings(); + Random rand = new(); _peers = Enumerable.Range(0, 5) .Select(_ => { - var peerIdentifier = PeerIdHelper.GetPeerId(rand.Next().ToString()); - var peer = new Peer {PeerId = peerIdentifier}; + var peerIdentifier = MultiAddressHelper.GetAddress(rand.Next().ToString()); + var peer = new Peer { Address = peerIdentifier, LastSeen = DateTime.UtcNow }; return peer; }).ToList(); var logger = Substitute.For(); - var peerRepository = Substitute.For(); - peerRepository.GetAll().Returns(_ => _peers); - - _previousDeltaHash = CidHelper.CreateCid(_hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32))); + _previousDeltaHash = + _hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32)).ToCid(); _producersByPreviousDelta = Substitute.For(); - _poaDeltaProducerProvider = new PoaDeltaProducersProvider(peerRepository, + var validatorSetStore = Substitute.For(); + validatorSetStore.Get(Arg.Any()).GetValidators().Returns(_peers.Select(x => x.Address.GetPublicKeyBytes().ToKvmAddress())); + + var deltaNumber = 0; + var deltaCache = Substitute.For(); + deltaCache.TryGetOrAddConfirmedDelta(Arg.Any(), out Arg.Any()) + .Returns(x => + { + x[1] = new Delta { DeltaNumber = deltaNumber++ }; + return true; + }); + + _poaDeltaProducerProvider = new PoaDeltaProducersProvider( peerSettings, _producersByPreviousDelta, _hashProvider, + validatorSetStore, + deltaCache, logger); } - [Fact] + [Test] public void GetDeltaProducersFromPreviousDelta_when_not_cached_should_store_and_return_an_ordered_list() { _producersByPreviousDelta.TryGetValue(Arg.Any(), out Arg.Any()).Returns(false); - var peers = _peers.Concat(new[] {_selfAsPeer}); + var peers = _peers.Select(x=>x.Address.GetKvmAddress()); var expectedProducers = peers.Select(p => { - var bytesToHash = p.PeerId.ToByteArray() - .Concat(_previousDeltaHash.ToArray()).ToArray(); - var ranking = _hashProvider.ComputeMultiHash(bytesToHash).ToArray(); + var ranking = _hashProvider.ComputeMultiHash(p.Bytes, _previousDeltaHash.ToArray()).ToArray(); return new { - PeerIdentifier = p.PeerId, + KvmAddress = p, ranking }; }) .OrderBy(h => h.ranking, ByteUtil.ByteListMinSizeComparer.Default) - .Select(h => h.PeerIdentifier) + .Select(h => h.KvmAddress) .ToList(); var producers = _poaDeltaProducerProvider.GetDeltaProducersFromPreviousDelta(_previousDeltaHash); @@ -116,19 +130,18 @@ public void GetDeltaProducersFromPreviousDelta_when_not_cached_should_store_and_ for (var i = 0; i < expectedProducers.Count; i++) { - producers[i].ToByteArray() - .Should().BeEquivalentTo(expectedProducers[i].ToByteArray()); + producers[i].Should().BeEquivalentTo(expectedProducers[i]); } } - [Fact] + [Test] public void GetDeltaProducersFromPreviousDelta_when_cached_should_not_recompute() { _producersByPreviousDelta.TryGetValue(Arg.Is(s => s.EndsWith(_previousDeltaHash)), out Arg.Any()) .Returns(ci => { - ci[1] = new List(); + ci[1] = new List
(); return true; }); diff --git a/src/Catalyst.Modules.POA.Consensus/Catalyst.Modules.POA.Consensus.csproj b/src/Catalyst.Modules.POA.Consensus/Catalyst.Modules.POA.Consensus.csproj index 6a0cfa1455..eaa47a05e0 100644 --- a/src/Catalyst.Modules.POA.Consensus/Catalyst.Modules.POA.Consensus.csproj +++ b/src/Catalyst.Modules.POA.Consensus/Catalyst.Modules.POA.Consensus.csproj @@ -1,11 +1,14 @@ - + - netcoreapp3.0 - James Kirby (nshcore@protonmail.com) + net6.0 + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Modules.POA.Consensus.snk true + + 1701;1702;CS8002 + Catalyst.Modules.POA.Consensus diff --git a/src/Catalyst.Modules.POA.Consensus/Deltas/PoaDeltaProducersProvider.cs b/src/Catalyst.Modules.POA.Consensus/Deltas/PoaDeltaProducersProvider.cs index 765bac9e9b..88e583f41e 100644 --- a/src/Catalyst.Modules.POA.Consensus/Deltas/PoaDeltaProducersProvider.cs +++ b/src/Catalyst.Modules.POA.Consensus/Deltas/PoaDeltaProducersProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,17 +25,19 @@ using System.Collections.Generic; using System.Linq; using System.Threading; +using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Abstractions.Hashing; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Abstractions.Validators; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Consensus.Deltas; -using Catalyst.Protocol.Peer; +using Catalyst.Protocol.Deltas; using Dawn; -using Google.Protobuf; -using LibP2P; +using Lib.P2P; using Microsoft.Extensions.Caching.Memory; using Microsoft.Extensions.Primitives; +using Nethermind.Core; using Serilog; using Peer = Catalyst.Core.Lib.P2P.Models.Peer; @@ -46,37 +48,41 @@ public class PoaDeltaProducersProvider : IDeltaProducersProvider private static string GetCacheKey(string rawKey) { return nameof(PoaDeltaProducersProvider) + "-" + rawKey; } private readonly ILogger _logger; - private readonly IMemoryCache _producersByPreviousDelta; private readonly MemoryCacheEntryOptions _cacheEntryOptions; private readonly Peer _selfAsPeer; private readonly IHashProvider _hashProvider; + private readonly IValidatorSetStore _validatorSetStore; + private readonly IDeltaCache _deltaCache; /// public IPeerRepository PeerRepository { get; } - public PoaDeltaProducersProvider(IPeerRepository peerRepository, - IPeerSettings peerSettings, + public PoaDeltaProducersProvider(IPeerSettings peerSettings, IMemoryCache producersByPreviousDelta, IHashProvider hashProvider, + IValidatorSetStore validatorSetStore, + IDeltaCache deltaCache, ILogger logger) { _logger = logger; - _selfAsPeer = new Peer {PeerId = peerSettings.PeerId}; - PeerRepository = peerRepository; + _selfAsPeer = new Peer { Address = peerSettings.Address }; _hashProvider = hashProvider; + _validatorSetStore = validatorSetStore; _cacheEntryOptions = new MemoryCacheEntryOptions() .AddExpirationToken( new CancellationChangeToken(new CancellationTokenSource(TimeSpan.FromMinutes(3)).Token)); _producersByPreviousDelta = producersByPreviousDelta; + _deltaCache = deltaCache; + } - public IList GetDeltaProducersFromPreviousDelta(Cid previousDeltaHash) + public IList
GetDeltaProducersFromPreviousDelta(Cid previousDeltaHash) { Guard.Argument(previousDeltaHash, nameof(previousDeltaHash)).NotNull(); if (_producersByPreviousDelta.TryGetValue(GetCacheKey(previousDeltaHash), - out IList cachedPeerIdsInPriorityOrder)) + out IList
cachedPeerIdsInPriorityOrder)) { _logger.Information("Retrieved favourite delta producers for successor of {0} from cache.", previousDeltaHash); @@ -86,29 +92,32 @@ public IList GetDeltaProducersFromPreviousDelta(Cid previousDeltaHash) _logger.Information("Calculating favourite delta producers for the successor of {0}.", previousDeltaHash); - var allPeers = PeerRepository.GetAll().Concat(new[] {_selfAsPeer}); + _deltaCache.TryGetOrAddConfirmedDelta(previousDeltaHash, out Delta delta); + + var validators = _validatorSetStore.Get(delta.DeltaNumber + 1).GetValidators(); + + var previous = previousDeltaHash.ToArray(); - var peerIdsInPriorityOrder = allPeers.Select(p => + var peerAddressesInPriorityOrder = validators.Select(address => { - var array = p.PeerId.ToByteArray().Concat(previousDeltaHash.ToArray()).ToArray(); - var ranking = _hashProvider.ComputeMultiHash(array).ToArray(); + var ranking = _hashProvider.ComputeMultiHash(address.Bytes, previous).ToArray(); return new { - p.PeerId, + address, ranking }; }) .OrderBy(h => h.ranking, ByteUtil.ByteListMinSizeComparer.Default) - .Select(h => h.PeerId) + .Select(h => h.address) .ToList(); _logger.Information("Adding favourite delta producers for the successor of {0} to cache.", previousDeltaHash); - _logger.Debug("Favourite producers are, in that order, [{0}]", string.Join(", ", peerIdsInPriorityOrder)); - _producersByPreviousDelta.Set(GetCacheKey(previousDeltaHash), peerIdsInPriorityOrder, + _logger.Debug("Favourite producers are, in that order, [{0}]", string.Join(", ", peerAddressesInPriorityOrder)); + _producersByPreviousDelta.Set(GetCacheKey(previousDeltaHash), peerAddressesInPriorityOrder, _cacheEntryOptions); - return peerIdsInPriorityOrder; + return peerAddressesInPriorityOrder; } } } diff --git a/src/Catalyst.Modules.POA.Consensus/PoaConsensusModule.cs b/src/Catalyst.Modules.POA.Consensus/PoaConsensusModule.cs index 3c1533dc79..4ac5649258 100644 --- a/src/Catalyst.Modules.POA.Consensus/PoaConsensusModule.cs +++ b/src/Catalyst.Modules.POA.Consensus/PoaConsensusModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,7 @@ namespace Catalyst.Modules.POA.Consensus { - public class PoaConsensusModule : Module + public class PoaConsensusModule : Autofac.Module { protected override void Load(ContainerBuilder builder) { diff --git a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests.csproj b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests.csproj index 5f3cd69f89..056d3930df 100644 --- a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests.csproj +++ b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.0 - James Kirby (nshcore@protonmail.com) + net6.0 + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Modules.POA.P2P.Tests.snk true @@ -9,6 +9,17 @@ Catalyst.Modules.POA.P2P.Tests + + 1701;1702;VSTHRD200;CS8002 + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + @@ -19,5 +30,4 @@ - diff --git a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/UnitTests/PeerHeartbeatCheckerTests.cs b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/UnitTests/PeerHeartbeatCheckerTests.cs index 200b2751a6..9dfa01de4b 100644 --- a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/UnitTests/PeerHeartbeatCheckerTests.cs +++ b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/UnitTests/PeerHeartbeatCheckerTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,18 +23,18 @@ using System; using System.Collections.Generic; -using System.Linq; using System.Threading.Tasks; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.P2P.Discovery; -using Catalyst.Core.Lib.P2P; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Core.Lib.P2P.Protocols; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Modules.POA.P2P.Discovery; using Catalyst.TestUtils; using NSubstitute; using Serilog; -using Xunit; +using NUnit.Framework; +using FluentAssertions; namespace Catalyst.Modules.POA.P2P.Tests.UnitTests { @@ -43,58 +43,72 @@ public sealed class PeerHeartbeatCheckerTests : IDisposable private const int PeerHeartbeatCheckSeconds = 3; private const int PeerChallengeTimeoutSeconds = 1; private IHealthChecker _peerHeartbeatChecker; - private readonly IPeerClient _peerClient; - private readonly IPeerRepository _peerRepository; - private readonly Peer _testPeer; + private IPeerClient _peerClient; + private IPeerRepository _peerRepository; + private Peer _testPeer; - public PeerHeartbeatCheckerTests() + [SetUp] + public void Init() { _peerRepository = Substitute.For(); _peerClient = Substitute.For(); _testPeer = new Peer { - PeerId = PeerIdHelper.GetPeerId("TestPeer") + Address = MultiAddressHelper.GetAddress("TestPeer") }; } - [Fact] - public async Task Can_Remove_Peer_On_Non_Responsive_Heartbeat() + //[Fact] + //public async Task Can_Remove_Peer_On_Non_Responsive_Heartbeat() + //{ + // await RunHeartbeatChecker().ConfigureAwait(false); + // _peerRepository.Received().Delete(_testPeer.DocumentId); + //} + + //[Fact] + //public async Task Can_Keep_Peer_On_Valid_Heartbeat_Response() + //{ + // await RunHeartbeatChecker(true).ConfigureAwait(false); + // _peerRepository.DidNotReceive().Delete(_testPeer.DocumentId); + //} + + [Test] + public async Task Can_Set_Peer_Awol_To_False_On_Non_Responsive_Heartbeat() { await RunHeartbeatChecker().ConfigureAwait(false); - _peerRepository.Received().Delete(_testPeer.DocumentId); + _testPeer.IsAwolPeer.Should().BeTrue(); } - [Fact] + [Test] public async Task Can_Keep_Peer_On_Valid_Heartbeat_Response() { await RunHeartbeatChecker(true).ConfigureAwait(false); - _peerRepository.DidNotReceive().Delete(_testPeer.DocumentId); + _testPeer.IsAwolPeer.Should().BeFalse(); } - [Fact] + [Test] public async Task Can_Remove_Peer_On_Max_Counter() { await RunHeartbeatChecker(maxNonResponsiveCounter: 2).ConfigureAwait(false); - _peerRepository.Received().Delete(_testPeer.DocumentId); + _testPeer.IsAwolPeer.Should().BeTrue(); } private async Task RunHeartbeatChecker(bool sendResponse = false, int maxNonResponsiveCounter = 1) { var peers = new List {_testPeer}; - var peerSettings = _testPeer.PeerId.ToSubstitutedPeerSettings(); - var peerChallenger = new PeerChallenger( - Substitute.For(), + var peerSettings = _testPeer.Address.ToSubstitutedPeerSettings(); + PeerChallengeRequest peerChallenger = new( + Substitute.For(), _peerClient, - peerSettings, + peerSettings, PeerChallengeTimeoutSeconds); if (sendResponse) { - peerChallenger.ChallengeResponseMessageStreamer.OnNext(new PeerChallengerResponse(_testPeer.PeerId)); + peerChallenger.ChallengeResponseMessageStreamer.OnNext(new PeerChallengeResponse(_testPeer.Address)); } _peerRepository.GetAll().Returns(peers); - _peerRepository.AsQueryable().Returns(peers.AsQueryable()); _peerHeartbeatChecker = new PeerHeartbeatChecker( Substitute.For(), _peerRepository, @@ -103,12 +117,10 @@ private async Task RunHeartbeatChecker(bool sendResponse = false, int maxNonResp maxNonResponsiveCounter); _peerHeartbeatChecker.Run(); - await Task.Delay(TimeSpan.FromSeconds(PeerHeartbeatCheckSeconds * (maxNonResponsiveCounter + 1)).Add(TimeSpan.FromSeconds(1))).ConfigureAwait(false); + await Task.Delay(TimeSpan.FromSeconds(PeerHeartbeatCheckSeconds * (maxNonResponsiveCounter + 1)) + .Add(TimeSpan.FromSeconds(1))).ConfigureAwait(false); } - void IDisposable.Dispose() - { - _peerHeartbeatChecker?.Dispose(); - } + void IDisposable.Dispose() { _peerHeartbeatChecker?.Dispose(); } } } diff --git a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/UnitTests/PoaDiscoveryTests.cs b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/UnitTests/PoaDiscoveryTests.cs index 94c62f841b..8e912f9cce 100644 --- a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/UnitTests/PoaDiscoveryTests.cs +++ b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests/UnitTests/PoaDiscoveryTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,44 +24,44 @@ using System.Threading.Tasks; using Catalyst.Abstractions.Hashing; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Modules.Hashing; using Catalyst.Modules.POA.P2P.Discovery; using Catalyst.TestUtils; +using MultiFormats.Registry; using Newtonsoft.Json; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; +using Catalyst.Abstractions.P2P; namespace Catalyst.Modules.POA.P2P.Tests.UnitTests { public sealed class PoaDiscoveryTests : FileSystemBasedTest { - private readonly IHashProvider _hashProvider; + private IHashProvider _hashProvider; - public PoaDiscoveryTests(ITestOutputHelper output) : base(output) + [SetUp] + public void Init() { - var hashingAlgorithm = HashingAlgorithm.GetAlgorithmMetadata("blake2b-256"); - _hashProvider = new HashProvider(hashingAlgorithm); + this.Setup(TestContext.CurrentContext); + _hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); } - [Fact] + [Test] public async Task Can_Populate_Peers_Correctly() { var peerRepository = Substitute.For(); - var pubkey = _hashProvider.ComputeUtf8MultiHash("hello").ToBase32(); var peers = new[] { - new PoaPeer {Ip = "92.207.178.198", Port = 42069, PublicKey = pubkey}, - new PoaPeer {Ip = "127.0.0.1", Port = 42069, PublicKey = pubkey} + new PoaPeer {Address="/ip4/192.168.0.181/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"}, + new PoaPeer {Address=MultiAddressHelper.GetAddress().ToString()}, }; await FileSystem.WriteTextFileToCddAsync(PoaDiscovery.PoaPeerFile, JsonConvert.SerializeObject(peers)); - var peerDiscovery = new PoaDiscovery(peerRepository, FileSystem, Substitute.For()); + PoaDiscovery peerDiscovery = new(Substitute.For(), peerRepository, FileSystem, Substitute.For()); await peerDiscovery.DiscoveryAsync().ConfigureAwait(false); peerRepository.Received(peers.Length).Add(Arg.Any()); } diff --git a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Catalyst.Modules.POA.P2P.Discovery.Consortium.csproj b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Catalyst.Modules.POA.P2P.Discovery.Consortium.csproj index f8f1688012..80e623c6ad 100644 --- a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Catalyst.Modules.POA.P2P.Discovery.Consortium.csproj +++ b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Catalyst.Modules.POA.P2P.Discovery.Consortium.csproj @@ -1,14 +1,17 @@ - + - netcoreapp3.0 + net6.0 Catalyst POA P2P Module - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Modules.POA.P2P.snk true true Catalyst.Modules.POA.P2P + + 1701;1702;CS8002 + diff --git a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Discovery/PeerHeartbeatChecker.cs b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Discovery/PeerHeartbeatChecker.cs index 997f509409..31db92df80 100644 --- a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Discovery/PeerHeartbeatChecker.cs +++ b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Discovery/PeerHeartbeatChecker.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,9 +25,9 @@ using System.Collections.Concurrent; using System.Reactive.Linq; using System.Threading.Tasks; -using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.P2P.Discovery; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Protocols; +using Catalyst.Abstractions.P2P.Repository; using Serilog; namespace Catalyst.Modules.POA.P2P.Discovery @@ -36,7 +36,7 @@ public sealed class PeerHeartbeatChecker : IHealthChecker { private readonly TimeSpan _checkHeartbeatInterval; private readonly IPeerRepository _peerRepository; - private readonly IPeerChallenger _peerChallenger; + private readonly IPeerChallengeRequest _peerChallengeRequest; private readonly int _maxNonResponsiveCounter; private readonly ConcurrentDictionary _nonResponsivePeerMap; private readonly ILogger _logger; @@ -44,7 +44,7 @@ public sealed class PeerHeartbeatChecker : IHealthChecker public PeerHeartbeatChecker(ILogger logger, IPeerRepository peerRepository, - IPeerChallenger peerChallenger, + IPeerChallengeRequest peerChallengeRequest, int checkHeartbeatIntervalSeconds, int maxNonResponsiveCounter) { @@ -52,7 +52,7 @@ public PeerHeartbeatChecker(ILogger logger, _nonResponsivePeerMap = new ConcurrentDictionary(); _peerRepository = peerRepository; _maxNonResponsiveCounter = maxNonResponsiveCounter; - _peerChallenger = peerChallenger; + _peerChallengeRequest = peerChallengeRequest; _checkHeartbeatInterval = TimeSpan.FromSeconds(checkHeartbeatIntervalSeconds); } @@ -70,25 +70,32 @@ private void CheckHeartbeat() { Task.Run(async () => { - var result = await _peerChallenger.ChallengePeerAsync(peer.PeerId).ConfigureAwait(false); + var result = await _peerChallengeRequest.ChallengePeerAsync(peer.Address); var counterValue = _nonResponsivePeerMap.GetOrAdd(peer.DocumentId, 0); _logger.Verbose( - $"Heartbeat result: {result.ToString()} Peer: {peer.PeerId} Non-Responsive Counter: {counterValue}"); + $"Heartbeat result: {result.ToString()} Peer: {peer.Address} Non-Responsive Counter: {counterValue}"); if (!result) { + // @TODO touch last seen on peer _nonResponsivePeerMap[peer.DocumentId] += 1; counterValue += 1; if (counterValue >= _maxNonResponsiveCounter) { - _peerRepository.Delete(peer.DocumentId); - _nonResponsivePeerMap.TryRemove(peer.DocumentId, out _); + // Remove all non POA nodes at the moment until node is using same p2p discovery + if (!peer.IsPoaNode) + { + _peerRepository.Delete(peer.DocumentId); + _nonResponsivePeerMap.TryRemove(peer.DocumentId, out _); + } _logger.Verbose( - $"Peer reached maximum non-responsive count: {peer.PeerId}. Evicted from repository"); + $"Peer reached maximum non-responsive count: {peer.Address}. Evicted from repository"); } } else { + peer.Touch(); + _peerRepository.Update(peer); _nonResponsivePeerMap[peer.DocumentId] = 0; } }).ConfigureAwait(false); diff --git a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Discovery/PoaDiscovery.cs b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Discovery/PoaDiscovery.cs index 7ea7e9adc4..1a5c653a15 100644 --- a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Discovery/PoaDiscovery.cs +++ b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/Discovery/PoaDiscovery.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,29 +29,33 @@ using Catalyst.Abstractions.FileSystem; using Catalyst.Abstractions.P2P.Discovery; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.Util; using Newtonsoft.Json; using Serilog; +using Catalyst.Abstractions.P2P; +using Catalyst.Core.Lib.Extensions; namespace Catalyst.Modules.POA.P2P.Discovery { public sealed class PoaDiscovery : IPeerDiscovery { public static string PoaPeerFile => "poa.nodes.json"; + private readonly IPeerSettings _peerSettings; private readonly IPeerRepository _peerRepository; private readonly IFileSystem _fileSystem; private readonly ILogger _logger; - public PoaDiscovery(IPeerRepository peerRepository, IFileSystem fileSystem, ILogger logger) + public PoaDiscovery(IPeerSettings peerSettings, IPeerRepository peerRepository, IFileSystem fileSystem, ILogger logger) { + _peerSettings = peerSettings; _peerRepository = peerRepository; _fileSystem = fileSystem; _logger = logger; } /// - /// @TODO get from container eventually + /// @TODO get from container eventually /// /// private string CopyPoaFile() @@ -74,11 +78,12 @@ public Task DiscoveryAsync() foreach (var peer in poaPeers.Select(poaPeer => new Peer { - PeerId = poaPeer.ToPeerId() + IsPoaNode = true, + Address = poaPeer.Address })) { _logger.Information( - $"Adding POA Peer: {peer.PeerId.IpAddress} Public Key: {peer.PeerId.PublicKey.KeyToString()}"); + $"Adding POA Peer: {peer.Address.GetIpAddress()} Public Key: {peer.Address.GetPublicKey()}"); if (!_peerRepository.Exists(peer.DocumentId)) { diff --git a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/PoaP2PModule.cs b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/PoaP2PModule.cs index dbd944bb04..ea7ff89e4c 100644 --- a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/PoaP2PModule.cs +++ b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/PoaP2PModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -33,10 +33,10 @@ protected override void Load(ContainerBuilder builder) { builder.RegisterType(); builder.RegisterType().As().SingleInstance(); - builder.RegisterType().As() - .WithParameter("checkHeartbeatIntervalSeconds", 10) - .WithParameter("maxNonResponsiveCounter", 5) - .SingleInstance(); + //builder.RegisterType().As() + // .WithParameter("checkHeartbeatIntervalSeconds", 10) + // .WithParameter("maxNonResponsiveCounter", 5) + // .SingleInstance(); } } } diff --git a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/PoaPeer.cs b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/PoaPeer.cs index adb32b10d2..d8f8895bf4 100644 --- a/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/PoaPeer.cs +++ b/src/Catalyst.Modules.POA.P2P.Discovery.Consortium/PoaPeer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,32 +21,17 @@ #endregion -using System; using System.Net; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Network; using Catalyst.Core.Lib.Util; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Modules.POA.P2P { - [Obsolete("Please use the PeerIdDao instead")] public sealed class PoaPeer { - public string Ip { get; set; } - - public int Port { get; set; } - - public string PublicKey { get; set; } - - public PeerId ToPeerId() - { - return new PeerId - { - PublicKey = PublicKey.KeyToByteString(), - Ip = IPAddress.Parse(Ip).To16Bytes().ToByteString(), - Port = (uint) Port - }; - } + public string Address { set; get; } } } diff --git a/src/Catalyst.Modules.Repository.CosmosDb/Catalyst.Modules.Repository.CosmosDb.csproj b/src/Catalyst.Modules.Repository.CosmosDb/Catalyst.Modules.Repository.CosmosDb.csproj index fa2c7c30af..0053d2b6bb 100644 --- a/src/Catalyst.Modules.Repository.CosmosDb/Catalyst.Modules.Repository.CosmosDb.csproj +++ b/src/Catalyst.Modules.Repository.CosmosDb/Catalyst.Modules.Repository.CosmosDb.csproj @@ -1,12 +1,15 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Modules.Repository.CosmosDb - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Modules.Repository.CosmosDb.snk true + + 1701;1702;CS8002 + diff --git a/src/Catalyst.Modules.Repository.CosmosDb/CosmosDbRepository.cs b/src/Catalyst.Modules.Repository.CosmosDb/CosmosDbRepository.cs index 3838787d24..4a39b832c0 100644 --- a/src/Catalyst.Modules.Repository.CosmosDb/CosmosDbRepository.cs +++ b/src/Catalyst.Modules.Repository.CosmosDb/CosmosDbRepository.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Modules.Repository.MongoDb/Catalyst.Modules.Repository.MongoDb.csproj b/src/Catalyst.Modules.Repository.MongoDb/Catalyst.Modules.Repository.MongoDb.csproj index 062c03b3c3..d24b50ba15 100644 --- a/src/Catalyst.Modules.Repository.MongoDb/Catalyst.Modules.Repository.MongoDb.csproj +++ b/src/Catalyst.Modules.Repository.MongoDb/Catalyst.Modules.Repository.MongoDb.csproj @@ -1,12 +1,15 @@  - netcoreapp3.0 + net6.0 Catalyst.Modules.Repository.MongoDb - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Modules.Repository.MongoDb.snk true + + 1701;1702;CS8002 + @@ -14,7 +17,7 @@ - + diff --git a/src/Catalyst.Modules.Repository.MongoDb/RepositoryMongoDbModule.cs b/src/Catalyst.Modules.Repository.MongoDb/RepositoryMongoDbModule.cs index 1fbd0c1132..f29a7dd8bb 100644 --- a/src/Catalyst.Modules.Repository.MongoDb/RepositoryMongoDbModule.cs +++ b/src/Catalyst.Modules.Repository.MongoDb/RepositoryMongoDbModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,20 +22,20 @@ #endregion using Autofac; -using Catalyst.Core.Lib.Mempool.Documents; +using Catalyst.Core.Lib.DAO.Transaction; using SharpRepository.MongoDbRepository; using SharpRepository.Repository; using SharpRepository.Repository.Caching; namespace Catalyst.Modules.Repository.MongoDb { - public class MempoolModule : Module + public class MongoDbMempoolModule : Module { protected override void Load(ContainerBuilder builder) { - builder.Register(c => new MongoDbRepository( - c.ResolveOptional>() - )).As>().SingleInstance(); - } + builder.Register(c => new MongoDbRepository( + c.ResolveOptional>() + )).As>().SingleInstance(); + } } } diff --git a/src/Catalyst.Modules.Repository.MongoDb/Util/BsonProtoObjectConverter.cs b/src/Catalyst.Modules.Repository.MongoDb/Util/BsonProtoObjectConverter.cs index 4b86d05e5c..d3cbb28213 100644 --- a/src/Catalyst.Modules.Repository.MongoDb/Util/BsonProtoObjectConverter.cs +++ b/src/Catalyst.Modules.Repository.MongoDb/Util/BsonProtoObjectConverter.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -36,7 +36,7 @@ namespace Catalyst.Modules.Repository.MongoDb.Util public object Deserialize(BsonDeserializationContext context, BsonDeserializationArgs args) { - var parser = new JsonParser(JsonParser.Settings.Default); + JsonParser parser = new(JsonParser.Settings.Default); var buffer = context.Reader.ReadRawBsonDocument(); var json = new RawBsonDocument(buffer).ToJson(); var jObject = JObject.Parse(json); @@ -45,7 +45,7 @@ public object Deserialize(BsonDeserializationContext context, BsonDeserializatio public void Serialize(BsonSerializationContext context, BsonSerializationArgs args, object value) { - var formatter = new JsonFormatter(JsonFormatter.Settings.Default); + JsonFormatter formatter = new(JsonFormatter.Settings.Default); string format = formatter.Format((T) value); var bsonDocument = BsonDocument.Parse(format); var raw = bsonDocument.ToBson(); diff --git a/src/Catalyst.Modules.Repository.MongoDb/Util/BsonSerializationProviders.cs b/src/Catalyst.Modules.Repository.MongoDb/Util/BsonSerializationProviders.cs index a18d221b4e..76c7ae7d73 100644 --- a/src/Catalyst.Modules.Repository.MongoDb/Util/BsonSerializationProviders.cs +++ b/src/Catalyst.Modules.Repository.MongoDb/Util/BsonSerializationProviders.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -37,10 +37,9 @@ public static void Init() AddSerializer(); AddSerializer(); AddSerializer(); - AddSerializer(); } - - static void AddSerializer() where TType : IMessage, new() + + private static void AddSerializer() where TType : IMessage, new() { BsonSerializer.RegisterSerializer(typeof(TType), new ProtoBsonSerializer()); diff --git a/src/Catalyst.Modules.Server.Blazor/App.razor b/src/Catalyst.Modules.Server.Blazor/App.razor index a8e95447c6..d463b2f7ea 100644 --- a/src/Catalyst.Modules.Server.Blazor/App.razor +++ b/src/Catalyst.Modules.Server.Blazor/App.razor @@ -1,10 +1,10 @@ - - - - - - -

Sorry, there's nothing at this address.

-
-
-
+@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@* *@ +@*

Sorry, there's nothing at this address.

*@ +@*
*@ +@*
*@ +@*
*@ diff --git a/src/Catalyst.Modules.Server.Blazor/AutofacServiceProvider.cs b/src/Catalyst.Modules.Server.Blazor/AutofacServiceProvider.cs index 4d1d1b8025..308e7467e8 100644 --- a/src/Catalyst.Modules.Server.Blazor/AutofacServiceProvider.cs +++ b/src/Catalyst.Modules.Server.Blazor/AutofacServiceProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -72,11 +72,11 @@ public IServiceProvider CreateServiceProvider(ContainerBuilder containerBuilder) /// /// /// - internal class AutofacServiceProvider : IServiceProvider, ISupportRequiredService, IDisposable + internal sealed class AutofacServiceProvider : IServiceProvider, ISupportRequiredService, IDisposable { public ILifetimeScope LifetimeScope { get; set; } - private bool _disposed = false; + private bool _disposed; /// /// Gets service of type from the @@ -115,14 +115,16 @@ internal class AutofacServiceProvider : IServiceProvider, ISupportRequiredServic /// to release both managed and unmanaged resources; /// to release only unmanaged resources. /// - protected virtual void Dispose(bool disposing) + private void Dispose(bool disposing) { - if (!_disposed) + if (_disposed) { - if (disposing) LifetimeScope.Dispose(); - - _disposed = true; + return; } + + if (disposing) LifetimeScope.Dispose(); + + _disposed = true; } /// diff --git a/src/Catalyst.Modules.Server.Blazor/BlazorServerModule.cs b/src/Catalyst.Modules.Server.Blazor/BlazorServerModule.cs index 7ae41cc7f8..f73e8335b7 100644 --- a/src/Catalyst.Modules.Server.Blazor/BlazorServerModule.cs +++ b/src/Catalyst.Modules.Server.Blazor/BlazorServerModule.cs @@ -1,7 +1,7 @@ -#region LICENSE +#region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,7 +32,7 @@ namespace Catalyst.Modules.Server.Blazor { - public class BlazorServerModule : Module + public sealed class BlazorServerModule : Module { public static void Main(string[] args) { } @@ -52,7 +52,14 @@ protected override void Load(ContainerBuilder builder) //Ignored exception as the server cannot start without container being built } - builder.RegisterBuildCallback(Start); + builder.RegisterBuildCallback(c => + { + // TODO TheNewAutonomy + // _autofacServiceProviderFactory.SetContainer(container); + // _ = container.Resolve().RunAsync().ConfigureAwait(false); + }); + + // builder.RegisterBuildCallback(Start); } private void Start(IContainer container) @@ -93,7 +100,7 @@ public void Configure(IApplicationBuilder app) // For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940 public void ConfigureServices(IServiceCollection services) { - var listener = new DiagnosticListener("Microsoft.AspNetCore"); + DiagnosticListener listener = new("Microsoft.AspNetCore"); services.AddSingleton(listener); services.AddSingleton(listener); services.AddRazorPages(); diff --git a/src/Catalyst.Modules.Server.Blazor/Catalyst.Modules.Server.Blazor.csproj b/src/Catalyst.Modules.Server.Blazor/Catalyst.Modules.Server.Blazor.csproj index a45b40af6b..54a99e377c 100644 --- a/src/Catalyst.Modules.Server.Blazor/Catalyst.Modules.Server.Blazor.csproj +++ b/src/Catalyst.Modules.Server.Blazor/Catalyst.Modules.Server.Blazor.csproj @@ -1,8 +1,11 @@ - + - netcoreapp3.0 + net6.0 + + 1701;1702;CS8002 + @@ -21,8 +24,8 @@ - - + + diff --git a/src/Catalyst.Modules.Server.Blazor/Components/SendTransactionForm.razor.cs b/src/Catalyst.Modules.Server.Blazor/Components/SendTransactionForm.razor.cs index 93a45e3abe..f76cc8a937 100644 --- a/src/Catalyst.Modules.Server.Blazor/Components/SendTransactionForm.razor.cs +++ b/src/Catalyst.Modules.Server.Blazor/Components/SendTransactionForm.razor.cs @@ -1,7 +1,7 @@ -#region LICENSE +#region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,18 +24,9 @@ using Catalyst.Abstractions.IO.Events; using Catalyst.Abstractions.KeySigner; using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.IO.Messaging.Correlation; using Catalyst.Modules.Server.Blazor.Models; -using Catalyst.Protocol.Cryptography; -using Catalyst.Protocol.Wire; -using Google.Protobuf; -using Google.Protobuf.WellKnownTypes; using Microsoft.AspNetCore.Components; using Microsoft.JSInterop; -using System; -using Catalyst.Core.Lib.Util; -using Catalyst.Protocol.Transaction; namespace Catalyst.Modules.Server.Blazor.Components { @@ -55,36 +46,6 @@ public class SendTransactionFormBase : ComponentBase [Inject] public ITransactionReceivedEvent TransactionReceivedEvent { get; set; } - public void HandleValidSubmit() - { - //TODO: Currently there is no method to actually set the transaction amount - var transaction = new TransactionBroadcast {Timestamp = Timestamp.FromDateTime(DateTime.UtcNow)}; - transaction.PublicEntries.Add(new PublicEntry - { - Base = new BaseEntry - { - SenderPublicKey = PeerSettings.PublicKey.KeyToByteString() - } - }); - var signingContext = new SigningContext - { - NetworkType = PeerSettings.NetworkType, - SignatureType = SignatureType.ProtocolPeer - }; - - var signature = KeySigner.Sign(transaction.ToByteArray(), signingContext); - transaction.Signature = new Signature - { - RawBytes = signature.SignatureBytes.ToByteString(), - SigningContext = signingContext - }; - var status = TransactionReceivedEvent - .OnTransactionReceived(transaction.ToProtocolMessage(PeerSettings.PeerId, - CorrelationId.GenerateCorrelationId())); - JsRuntime.InvokeAsync("window.alert", $"Transaction Status: {status.ToString()}") - .ConfigureAwait(false); - Model = new SendTransactionModel(); - StateHasChanged(); - } + public void HandleValidSubmit() { } } } diff --git a/src/Catalyst.Modules.Server.Blazor/Components/TransactionTableComponent.razor b/src/Catalyst.Modules.Server.Blazor/Components/TransactionTableComponent.razor index d042256f52..11c729fdda 100644 --- a/src/Catalyst.Modules.Server.Blazor/Components/TransactionTableComponent.razor +++ b/src/Catalyst.Modules.Server.Blazor/Components/TransactionTableComponent.razor @@ -2,6 +2,6 @@ @Model.Id - @Model.TimeStamp - @GetAmount() + @* @Model.TimeStamp *@ + @* @GetAmount() *@ diff --git a/src/Catalyst.Modules.Server.Blazor/Components/TransactionTableComponent.razor.cs b/src/Catalyst.Modules.Server.Blazor/Components/TransactionTableComponent.razor.cs index 87982952f6..b2d9eb8d0e 100644 --- a/src/Catalyst.Modules.Server.Blazor/Components/TransactionTableComponent.razor.cs +++ b/src/Catalyst.Modules.Server.Blazor/Components/TransactionTableComponent.razor.cs @@ -1,7 +1,7 @@ -#region LICENSE +#region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,6 @@ #endregion -using System; -using System.Linq; -using System.Threading; using Catalyst.Core.Lib.DAO; using Microsoft.AspNetCore.Components; @@ -32,7 +29,5 @@ namespace Catalyst.Modules.Server.Blazor.Components public class TransactionTableComponentBase : ComponentBase { [Parameter] public TransactionBroadcastDao Model { get; set; } - - public string GetAmount() { return Model.PublicEntries.First().Amount; } } } diff --git a/src/Catalyst.Modules.Server.Blazor/Models/SendTransactionModel.cs b/src/Catalyst.Modules.Server.Blazor/Models/SendTransactionModel.cs index 4632ec5642..7bf08e0f04 100644 --- a/src/Catalyst.Modules.Server.Blazor/Models/SendTransactionModel.cs +++ b/src/Catalyst.Modules.Server.Blazor/Models/SendTransactionModel.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Modules.Server.Blazor/Pages/MempoolViewer.razor b/src/Catalyst.Modules.Server.Blazor/Pages/MempoolViewer.razor index ad510261ac..cf4dd25cee 100644 --- a/src/Catalyst.Modules.Server.Blazor/Pages/MempoolViewer.razor +++ b/src/Catalyst.Modules.Server.Blazor/Pages/MempoolViewer.razor @@ -1,14 +1,14 @@ @page "/mempool" @using System.Threading -@using Catalyst.Abstractions.Mempool.Repositories +@* @using Catalyst.Abstractions.Mempool.Repositories *@ @using Catalyst.Core.Lib.DAO @implements IDisposable -@inject IMempoolRepository MempoolRepository +@* @inject IMempoolRepository MempoolRepository *@

Mempool Viewer

-

Current count: @MempoolRepository.Count()

+@*

Current count: @MempoolRepository.Count()

*@ @@ -37,9 +37,9 @@ _mempoolList = new List(); _timer = new Timer((_) => { - _mempoolList = MempoolRepository.GetAll().ToList(); + // _mempoolList = MempoolRepository.GetAll().ToList(); InvokeAsync(StateHasChanged).ConfigureAwait(false); - }, null, 1000, 1000); + }, null, (long) 1000, 1000); } public void Dispose() { _timer?.Dispose(); } diff --git a/src/Catalyst.Modules.UPnP.Tests/Catalyst.Modules.UPnP.Tests.csproj b/src/Catalyst.Modules.UPnP.Tests/Catalyst.Modules.UPnP.Tests.csproj new file mode 100644 index 0000000000..0b330d747b --- /dev/null +++ b/src/Catalyst.Modules.UPnP.Tests/Catalyst.Modules.UPnP.Tests.csproj @@ -0,0 +1,28 @@ + + + net6.0 + Catalyst.UPnP.Tests + Fran (fran.sl@catalystnet.org) + true + Catalyst.Modules.UPnP.Tests.snk + true + + + 1701;1702;VSTHRD200;CS8002 + + + + + + + + + + + + + + + + + diff --git a/src/Catalyst.Modules.UPnP.Tests/Catalyst.Modules.UPnP.Tests.snk b/src/Catalyst.Modules.UPnP.Tests/Catalyst.Modules.UPnP.Tests.snk new file mode 100644 index 0000000000..e364356b76 Binary files /dev/null and b/src/Catalyst.Modules.UPnP.Tests/Catalyst.Modules.UPnP.Tests.snk differ diff --git a/src/Catalyst.Modules.UPnP.Tests/Config/devnet.json b/src/Catalyst.Modules.UPnP.Tests/Config/devnet.json new file mode 100644 index 0000000000..2b22f97322 --- /dev/null +++ b/src/Catalyst.Modules.UPnP.Tests/Config/devnet.json @@ -0,0 +1,74 @@ +{ + "CatalystNodeConfiguration": { + "Peer": { + "PublicKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", + "Port": 42076, + "BindAddress": "127.0.0.1", + "PublicIpAddress": "127.0.0.1", + "DnsServers": [ + "127.0.0.1:5053" + ], + "SeedServers": [ + "seed1.catalystnetwork.io", + "seed2.catalystnetwork.io", + "seed3.catalystnetwork.io", + "seed4.catalystnetwork.io", + "seed5.catalystnetwork.io" + ] + }, + "Rpc": { + "BindAddress": "127.0.0.1", + "Port": 42066, + "PfxFileName": "mycert.pfx" + }, + "PersistenceConfiguration": { + "repositories": { + "default": "inMemoryNoCaching", + "inMemory": { + "factory": "SharpRepository.InMemoryRepository.InMemoryConfigRepositoryFactory, SharpRepository.InMemoryRepository" + }, + "mongoDb": { + "factory": "SharpRepository.MongoDbRepository.MongoDbConfigRepositoryFactory, SharpRepository.MongoDbRepository", + "connectionString": "mongodb:://127.0.0.1" + }, + "inMemoryNoCaching": { + "factory": "SharpRepository.InMemoryRepository.InMemoryConfigRepositoryFactory, SharpRepository.InMemoryRepository", + "cachingStrategy": "none", + "cachingProvider": "noCachingProvider" + } + }, + "cachingProviders": { + "default": "inMemoryProvider", + "inMemoryProvider": { + "factory": "SharpRepository.Repository.Caching.InMemoryConfigCachingProviderFactory, SharpRepository.Repository" + }, + "noCachingProvider": { + "factory": "SharpRepository.Repository.Caching.NoCachingConfigCachingProviderFactory, SharpRepository.Repository" + } + }, + "cachingStrategies": { + "default": "standard", + "standard": { + "factory": "SharpRepository.Repository.Caching.StandardConfigCachingStrategyFactory, SharpRepository.Repository", + "generational": "true", + "writeThrough": "false" + }, + //"redisProvider": { + // "factory": "SharpRepository.Caching.Redis.RedisConfigCachingProviderFactory, SharpRepository.Caching.Redis", + // "host": "127.0.0.1", + // "port": "6379", + // "ssl": false, + // "password": "", + // "defaultDatabase": "0" + //}, + "timeout": { + "factory": "SharpRepository.Repository.Caching.TimeoutConfigCachingStrategyFactory, SharpRepository.Repository", + "timeout": "200" + }, + "none": { + "factory": "SharpRepository.Repository.Caching.NoCachingConfigCachingStrategyFactory, SharpRepository.Repository" + } + } + } + } +} diff --git a/src/Catalyst.Modules.UPnP.Tests/IntegrationTests/IntegrationTests.cs b/src/Catalyst.Modules.UPnP.Tests/IntegrationTests/IntegrationTests.cs new file mode 100644 index 0000000000..9c7ee85a45 --- /dev/null +++ b/src/Catalyst.Modules.UPnP.Tests/IntegrationTests/IntegrationTests.cs @@ -0,0 +1,80 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading.Tasks; +using Catalyst.TestUtils; +using Catalyst.UPnP.Tests.Utils; +using FluentAssertions; +using Mono.Nat; +using NSubstitute; +using NUnit.Framework; +using Serilog; + + + + +namespace Catalyst.Modules.UPnP.Tests.IntegrationTests +{ + [TestFixture] + [Category(Traits.IntegrationTest)] + public class PortMapperTests + { + private ILogger _logger; + + [SetUp] + public void Init() + { + _logger = Substitute.For(); + } + + [Test] + public async Task Can_Add_Ports_From_Devnet_File() + { + Mapping[] existingMappings = {}; + var device = new TestNatDeviceWithInternalMappings(existingMappings); + + device.GetAllMappingsAsync().Result.Should().HaveCount(0); + + await Program.Start(new [] {"--filepath", "./Config/devnet.json"}, _logger, new TestNatUtilityProvider(device)); + + device.GetAllMappingsAsync().Result.Should().HaveCount(2); + } + + [Test] + public async Task Can_Add_Ports_From_Devnet_File_And_Delete() + { + Mapping[] existingMappings = {}; + var device = new TestNatDeviceWithInternalMappings(existingMappings); + + device.GetAllMappingsAsync().Result.Should().HaveCount(0); + + await Program.Start(new string[] {"--filepath", "./Config/devnet.json"}, _logger, new TestNatUtilityProvider(device)); + + device.GetAllMappingsAsync().Result.Should().HaveCount(2); + + await Program.Start(new string[] {"--filepath", "./Config/devnet.json", "--delete"}, _logger, new TestNatUtilityProvider(device)); + + device.GetAllMappingsAsync().Result.Should().HaveCount(0); + } + } +} diff --git a/src/Catalyst.Modules.UPnP.Tests/UnitTests/PortMapperTests.cs b/src/Catalyst.Modules.UPnP.Tests/UnitTests/PortMapperTests.cs new file mode 100644 index 0000000000..ed83964ecf --- /dev/null +++ b/src/Catalyst.Modules.UPnP.Tests/UnitTests/PortMapperTests.cs @@ -0,0 +1,176 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading; +using System.Threading.Tasks; +using Catalyst.UPnP.Tests.Utils; +using FluentAssertions; +using Mono.Nat; +using NSubstitute; +using NUnit.Framework; +using Serilog; + +namespace Catalyst.Modules.UPnP.Tests.UnitTests +{ + public class PortMapperTests + { + private const int SecondsTimeout = 5; + private readonly ILogger _logger = Substitute.For(); + private Mapping _mappingA; + private Mapping _mappingB; + private Mapping _mappingC; + + [SetUp] + public void Init() + { + const int portA = 6024; + const int portB = 6025; + _mappingA = new Mapping(Mono.Nat.Protocol.Tcp, portA, portA); + _mappingB = new Mapping(Mono.Nat.Protocol.Tcp, portB, portB); + _mappingC = new Mapping(Mono.Nat.Protocol.Tcp, portB, portA); + } + + [Test] + public async Task PortMapper_Stops_Searching_After_Timeout() + { + var natUtilityProvider = Substitute.For(); + var portMapper = new PortMapper(natUtilityProvider, _logger); + var outcome = await portMapper.MapPorts(new Mapping[]{}, new CancellationTokenSource(), SecondsTimeout); + outcome.Should().Be(PortMapperConstants.Result.Timeout); + natUtilityProvider.Received(1).StartDiscovery(); + natUtilityProvider.Received(1).StopDiscovery(); + } + + [Test] + public async Task PortMapper_Stops_Searching_After_Task_Finished() + { + var device = Substitute.For(); + var natUtilityProvider = new TestNatUtilityProvider(device); + var portMapper = new PortMapper(natUtilityProvider, _logger); + var outcome = await portMapper.MapPorts(new Mapping[]{}, new CancellationTokenSource(), SecondsTimeout); + outcome.Should().Be(PortMapperConstants.Result.TaskFinished); + } + + [Test] + public async Task Adds_Mapping_If_Mapping_Doesnt_Exist() + { + Mapping[] existingMappings = {_mappingA}; + Mapping[] attemptedMappings = {_mappingB}; + + var device = Utils.GetTestDeviceWithExistingMappings(existingMappings); + + var portMapper = new PortMapper(new TestNatUtilityProvider(device), _logger); + + await portMapper.MapPorts(attemptedMappings, new CancellationTokenSource(), SecondsTimeout); + await device.Received(1).CreatePortMapAsync(Arg.Is(attemptedMappings[0])); + } + + [Test] + public async Task Can_Add_Multiple_Mappings() + { + Mapping[] existingMappings = {}; + Mapping[] attemptedMappings = {_mappingA,_mappingB}; + + var device = Utils.GetTestDeviceWithExistingMappings(existingMappings); + + var portMapper = new PortMapper(new TestNatUtilityProvider(device), _logger); + + await portMapper.MapPorts(attemptedMappings, new CancellationTokenSource(), SecondsTimeout); + await device.Received(1).CreatePortMapAsync(_mappingA); + await device.Received(1).CreatePortMapAsync(_mappingB); + } + + [Test] + public async Task Does_Not_Add_Mapping_If_Mapping_Exists() + { + + Mapping[] existingMappings = {_mappingA, _mappingB}; + Mapping[] attemptedMappings = {_mappingA}; + + var device = Utils.GetTestDeviceWithExistingMappings(existingMappings); + + var portMapper = new PortMapper(new TestNatUtilityProvider(device), _logger); + await portMapper.MapPorts(attemptedMappings, new CancellationTokenSource(), SecondsTimeout); + + await device.Received(0).CreatePortMapAsync(Arg.Is(attemptedMappings[0])); + } + + [Test] + public async Task Does_Not_Add_Mapping_If_Mapping_Partially_Exists() + { + Mapping[] existingMappings = {_mappingA}; + Mapping[] attemptedMappings = {_mappingC}; //mapping C has one port shared with mapping A + + var device = Utils.GetTestDeviceWithExistingMappings(existingMappings); + + var portMapper = new PortMapper(new TestNatUtilityProvider(device), _logger); + await portMapper.MapPorts(attemptedMappings, new CancellationTokenSource(), SecondsTimeout); + + await device.Received(0).CreatePortMapAsync(Arg.Is(attemptedMappings[0])); + } + + [Test] + public async Task If_Alternative_Mapping_Added_By_Device_It_Is_Subsequently_Deleted() + { + Mapping[] existingMappings = {}; + Mapping[] attemptedMappings = {_mappingA}; + + var device = Utils.GetTestDeviceWithExistingMappings(existingMappings); + device.CreatePortMapAsync(_mappingA) + .Returns(Task.FromResult(_mappingB)); + + var portMapper = new PortMapper(new TestNatUtilityProvider(device), _logger); + + await portMapper.MapPorts(attemptedMappings, new CancellationTokenSource(), SecondsTimeout); + await device.Received(1).DeletePortMapAsync(_mappingB); + } + + [Test] + public async Task Can_Remove_Mapped_Port_If_Mapping_Exists() + { + Mapping[] existingMappings = {_mappingA}; + Mapping[] mappingsToDelete = {_mappingA}; + + var device = Utils.GetTestDeviceWithExistingMappings(existingMappings); + + var portMapper = new PortMapper(new TestNatUtilityProvider(device), _logger); + + await portMapper.MapPorts(mappingsToDelete, new CancellationTokenSource(), SecondsTimeout, true); + await device.Received(1).DeletePortMapAsync(Arg.Is(_mappingA)); + } + + [Test] + public async Task Does_Not_Attempt_Remove_Mapped_Port_If_Mapping_Doesnt_Exist() + { + Mapping[] existingMappings = {_mappingA}; + Mapping[] mappingsToDelete = {_mappingB}; + + var device = Utils.GetTestDeviceWithExistingMappings(existingMappings); + + var portMapper = new PortMapper(new TestNatUtilityProvider(device), _logger); + + await portMapper.MapPorts(mappingsToDelete, new CancellationTokenSource(), SecondsTimeout, true); + await device.Received(0).DeletePortMapAsync(Arg.Is(_mappingB)); + } + } +} diff --git a/src/Catalyst.Modules.UPnP.Tests/UnitTests/PortMappingParserTests.cs b/src/Catalyst.Modules.UPnP.Tests/UnitTests/PortMappingParserTests.cs new file mode 100644 index 0000000000..cfbd9e37b9 --- /dev/null +++ b/src/Catalyst.Modules.UPnP.Tests/UnitTests/PortMappingParserTests.cs @@ -0,0 +1,72 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ +#endregion + +using FluentAssertions; +using Mono.Nat; +using NUnit.Framework; + +namespace Catalyst.Modules.UPnP.Tests.UnitTests +{ + public class PortMappingParserTests + { + + [Test] + public void Can_Parse_Using_Default_Identifiers() + { + var json = @"{'CatalystNodeConfiguration': { 'Peer': {'Port': 42076},'Rpc': {'Port': 42066}}}"; + var mappings = PortMappingParser.ParseJson(PortMapperConstants.DefaultTcpProperty, PortMapperConstants.DefaultUdpProperty, + json); + mappings.Should().Contain(new Mapping(Mono.Nat.Protocol.Tcp, 42076, 42076)); + mappings.Should().Contain(new Mapping(Mono.Nat.Protocol.Udp, 42066, 42066)); + } + + [Test] + public void Can_Parse_With_Non_Default_Identifiers() + { + var json = @"{'Peer': { 'Port': {'Port1': 42076}}}"; + var mappings = PortMappingParser.ParseJson("Peer.Port.Port1", PortMapperConstants.DefaultUdpProperty, + json); + mappings.Should().Contain(new Mapping(Mono.Nat.Protocol.Tcp, 42076, 42076)); + } + + [Test] + public void Can_Parse_With_Multiple_Comma_Separated_Identifiers() + { + var json = @"{'Peer': { 'Port': {'Port1': 42076, 'Port2': 27676}}}"; + var mappings = PortMappingParser.ParseJson("Peer.Port.Port1,Peer.Port.Port2", PortMapperConstants.DefaultUdpProperty, + json); + mappings.Should().Contain(new Mapping(Mono.Nat.Protocol.Tcp, 42076, 42076)); + mappings.Should().Contain(new Mapping(Mono.Nat.Protocol.Tcp, 27676, 27676)); + } + + [Test] + public void When_Identifiers_Do_Not_Exist_In_Json_Mapping_Is_Empty() + { + var json = @"{}"; + var mappings = PortMappingParser.ParseJson(PortMapperConstants.DefaultTcpProperty, PortMapperConstants.DefaultUdpProperty, + json); + mappings.Should().HaveCount(0); + } + } + + +} diff --git a/src/Catalyst.Modules.UPnP.Tests/Utils/TestDeviceWithExistingMappings.cs b/src/Catalyst.Modules.UPnP.Tests/Utils/TestDeviceWithExistingMappings.cs new file mode 100644 index 0000000000..928f68c27a --- /dev/null +++ b/src/Catalyst.Modules.UPnP.Tests/Utils/TestDeviceWithExistingMappings.cs @@ -0,0 +1,39 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ +#endregion + +using Mono.Nat; +using NSubstitute; + +namespace Catalyst.UPnP.Tests.Utils +{ + public static class Utils + { + public static INatDevice GetTestDeviceWithExistingMappings(Mapping[] existingMappings) + { + var device = Substitute.For(); + device.GetAllMappingsAsync().Returns(existingMappings); + device.CreatePortMapAsync(Arg.Any()).ReturnsForAnyArgs(x => x.Arg()); + device.DeletePortMapAsync(Arg.Any()).ReturnsForAnyArgs(x => x.Arg()); + return device; + } + } +} diff --git a/src/Catalyst.Modules.UPnP.Tests/Utils/TestNatDeviceWithInternalMappings.cs b/src/Catalyst.Modules.UPnP.Tests/Utils/TestNatDeviceWithInternalMappings.cs new file mode 100644 index 0000000000..870d1dbb85 --- /dev/null +++ b/src/Catalyst.Modules.UPnP.Tests/Utils/TestNatDeviceWithInternalMappings.cs @@ -0,0 +1,73 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading.Tasks; +using Mono.Nat; + +namespace Catalyst.UPnP.Tests.Utils +{ + public class TestNatDeviceWithInternalMappings : INatDevice + { + private readonly List _mappings; + + public TestNatDeviceWithInternalMappings(IEnumerable existingMappings) + { + _mappings = existingMappings.ToList(); + } + + public Task CreatePortMapAsync(Mapping mapping) + { + _mappings.Add(mapping); + return Task.FromResult(mapping); + } + + public Task DeletePortMapAsync(Mapping mapping) + { + if (!_mappings.Contains(mapping)) return null; + _mappings.Remove(mapping); + return Task.FromResult(mapping); + } + + public Task GetAllMappingsAsync() + { + return Task.FromResult(_mappings.ToArray()); + } + + public Task GetExternalIPAsync() + { + throw new NotImplementedException(); + } + + public Task GetSpecificMappingAsync(Mono.Nat.Protocol protocol, int publicPort) + { + throw new NotImplementedException(); + } + + public IPEndPoint DeviceEndpoint { get; } + public DateTime LastSeen { get; } + public NatProtocol NatProtocol { get; } + } +} diff --git a/src/Catalyst.Modules.UPnP.Tests/Utils/TestNatUtilityProvider.cs b/src/Catalyst.Modules.UPnP.Tests/Utils/TestNatUtilityProvider.cs new file mode 100644 index 0000000000..f76e8c8452 --- /dev/null +++ b/src/Catalyst.Modules.UPnP.Tests/Utils/TestNatUtilityProvider.cs @@ -0,0 +1,47 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ +#endregion + +using System; +using Catalyst.Modules.UPnP; +using Mono.Nat; + +namespace Catalyst.UPnP.Tests.Utils +{ + public class TestNatUtilityProvider : INatUtilityProvider + { + private readonly INatDevice _device; + public TestNatUtilityProvider(INatDevice device) + { + _device = device; + } + public event EventHandler DeviceFound; + public void StartDiscovery() + { + DeviceFound?.Invoke(this, new DeviceEventArgs(_device)); + } + + public void StopDiscovery() + { + } + } + +} diff --git a/src/Catalyst.Modules.UPnP/Catalyst.Modules.UPnP.csproj b/src/Catalyst.Modules.UPnP/Catalyst.Modules.UPnP.csproj new file mode 100644 index 0000000000..487b4d1728 --- /dev/null +++ b/src/Catalyst.Modules.UPnP/Catalyst.Modules.UPnP.csproj @@ -0,0 +1,24 @@ + + + net6.0 + UPnP CLI + CLI tool to set up port forwarding + Copyright © 2022 catalystnet.org + Exe + true + Catalyst.Modules.UPnP.snk + true + true + Catalyst.Modules.UPnP + + + 1701;1702;CS8002 + + + + + + + + + diff --git a/src/Catalyst.Modules.UPnP/Catalyst.Modules.UPnP.snk b/src/Catalyst.Modules.UPnP/Catalyst.Modules.UPnP.snk new file mode 100644 index 0000000000..0d0c4757c2 Binary files /dev/null and b/src/Catalyst.Modules.UPnP/Catalyst.Modules.UPnP.snk differ diff --git a/src/Catalyst.Abstractions/Mempool/Documents/IMempoolDocument.cs b/src/Catalyst.Modules.UPnP/INatUtilityProvider.cs similarity index 74% rename from src/Catalyst.Abstractions/Mempool/Documents/IMempoolDocument.cs rename to src/Catalyst.Modules.UPnP/INatUtilityProvider.cs index eff9b22e41..0ad1ae3175 100644 --- a/src/Catalyst.Abstractions/Mempool/Documents/IMempoolDocument.cs +++ b/src/Catalyst.Modules.UPnP/INatUtilityProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -18,16 +18,20 @@ * You should have received a copy of the GNU General Public License * along with Catalyst.Node. If not, see . */ - #endregion -using Catalyst.Abstractions.Mempool.Models; -using Catalyst.Abstractions.Repository; -namespace Catalyst.Abstractions.Mempool.Documents +using System; +using Mono.Nat; + +namespace Catalyst.Modules.UPnP { - public interface IMempoolDocument : IMempoolItem, IDocument + public interface INatUtilityProvider { - new string DocumentId { get; } + event EventHandler DeviceFound; + + void StartDiscovery(); + + void StopDiscovery(); } } diff --git a/src/Catalyst.Core.Lib/Util/CidHelper.cs b/src/Catalyst.Modules.UPnP/NatUtilityProvider.cs similarity index 60% rename from src/Catalyst.Core.Lib/Util/CidHelper.cs rename to src/Catalyst.Modules.UPnP/NatUtilityProvider.cs index abc193e6f5..c6d37004e0 100644 --- a/src/Catalyst.Core.Lib/Util/CidHelper.cs +++ b/src/Catalyst.Modules.UPnP/NatUtilityProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -18,24 +18,28 @@ * You should have received a copy of the GNU General Public License * along with Catalyst.Node. If not, see . */ - #endregion -using LibP2P; -using TheDotNetLeague.MultiFormats.MultiBase; -using TheDotNetLeague.MultiFormats.MultiHash; +using System; +using Mono.Nat; -namespace Catalyst.Core.Lib.Util +namespace Catalyst.Modules.UPnP { - public static class CidHelper + public class NatUtilityProvider : INatUtilityProvider { - public static readonly string Encoding = "base32"; + public event EventHandler DeviceFound; + + public void StartDiscovery() + { + NatUtility.DeviceFound += DeviceFound; + NatUtility.StartDiscovery(); + } - public static Cid CreateCid(MultiHash multiHash) + public void StopDiscovery() { - return new Cid {Version = 1, Hash = multiHash, ContentType = "raw", Encoding = Encoding}; + NatUtility.DeviceFound -= DeviceFound; + NatUtility.StopDiscovery(); } - public static Cid Cast(byte[] cid) { return Cid.Decode(MultiBase.Encode(cid, Encoding)); } } } diff --git a/src/Catalyst.Modules.UPnP/PortMapper.cs b/src/Catalyst.Modules.UPnP/PortMapper.cs new file mode 100644 index 0000000000..d0b9407040 --- /dev/null +++ b/src/Catalyst.Modules.UPnP/PortMapper.cs @@ -0,0 +1,152 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ +#endregion + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Mono.Nat; +using Serilog; + +namespace Catalyst.Modules.UPnP +{ + public sealed class PortMapper + { + private readonly SemaphoreSlim _locker = new(1, 1); + private readonly INatUtilityProvider _natUtilityProvider; + private readonly ILogger _logger; + + public PortMapper(INatUtilityProvider natUtilityProvider, ILogger logger) + { + _natUtilityProvider = natUtilityProvider; + _logger = logger; + } + + public Task MapPorts(Mapping[] ports, CancellationTokenSource tokenSource, + int timeoutInSeconds = PortMapperConstants.DefaultTimeout, bool delete = false) + { + var remappingLogic = delete ? (Func)DeletePortMappingsIfExisting : AddMappingsIfNotPreExisting; + return PerformEventOnDeviceFound(timeoutInSeconds, tokenSource.Token, device => ReMapPorts(device, ports, tokenSource, remappingLogic)); + } + + private async Task PerformEventOnDeviceFound(int timeoutInSeconds, CancellationToken token, Action onDeviceFound) + { + void DeviceFoundFunc(object o, DeviceEventArgs args) => onDeviceFound(args.Device); + _natUtilityProvider.DeviceFound += DeviceFoundFunc; + + _natUtilityProvider.StartDiscovery(); + _logger.Information(PortMapperConstants.StartedSearching); + + var result = await DelayUntilTimeoutOrMappingTaskEnds(timeoutInSeconds, token); + + _natUtilityProvider.DeviceFound -= DeviceFoundFunc; + + _natUtilityProvider.StopDiscovery(); + _logger.Information(PortMapperConstants.StoppedSearching); + + if(result==PortMapperConstants.Result.Timeout){_logger.Information(PortMapperConstants.CouldNotCommunicateWithRouter);} + + return result; + } + + private static async Task DelayUntilTimeoutOrMappingTaskEnds(int timeoutInSeconds, CancellationToken token) + { + try + { + await Task.Delay(TimeSpan.FromSeconds(timeoutInSeconds), token); + } + catch(TaskCanceledException) + { + return PortMapperConstants.Result.TaskFinished; + } + + return PortMapperConstants.Result.Timeout; + } + + private async void ReMapPorts(INatDevice device, Mapping[] newMappings, CancellationTokenSource tokenSource, Func remappingLogic) + { + await _locker.WaitAsync(); + try + { + var existingMappings = await device.GetAllMappingsAsync(); + await remappingLogic(existingMappings, newMappings, device); + } + catch(Exception e) + { + _logger.Information(PortMapperConstants.CouldNotCommunicateWithRouterException, e.ToString()); + } + finally + { + tokenSource.Cancel(); + _locker.Release(); + } + } + + private async Task DeletePortMappingsIfExisting(Mapping[] existingMappings, Mapping[] newMappings, INatDevice device) + { + foreach (var newMapping in newMappings) + { + if (Array.Exists(existingMappings, m => m.Equals(newMapping))) + { + var portDeleted = await device.DeletePortMapAsync(newMapping); + _logger.Information( + portDeleted.Equals(newMapping) + ? PortMapperConstants.DeletedMapping + : PortMapperConstants.CouldNotDeleteMapping, newMapping.Protocol, newMapping.PublicPort, newMapping.PrivatePort); + } + else + { + _logger.Information(PortMapperConstants.NoExistingMapping + , + newMapping.Protocol, newMapping.PublicPort, newMapping.PrivatePort); + } + } + } + + private async Task AddMappingsIfNotPreExisting(Mapping[] existingMappings, Mapping[] newMappings, INatDevice device) + { + foreach (var newMapping in newMappings) + { + if (!existingMappings.Any(m => m.PrivatePort==newMapping.PrivatePort || m.PublicPort==newMapping.PublicPort)) + { + var portMapped = await device.CreatePortMapAsync(newMapping); + if (portMapped.Equals(newMapping)) + { + _logger.Information(PortMapperConstants.CreatedMapping, newMapping.Protocol, newMapping.PublicPort, newMapping.PrivatePort); + } + else + { + await device.DeletePortMapAsync(portMapped); + _logger.Information(PortMapperConstants.CouldNotCreateMapping, newMapping.Protocol, newMapping.PublicPort, newMapping.PrivatePort); + } + } + else + { + _logger.Information( + PortMapperConstants.ConflictingMappingExists, + newMapping.Protocol, newMapping.PublicPort, newMapping.PrivatePort); + } + } + } + } + +} diff --git a/src/Catalyst.Modules.UPnP/PortMapperConstants.cs b/src/Catalyst.Modules.UPnP/PortMapperConstants.cs new file mode 100644 index 0000000000..25b2be6851 --- /dev/null +++ b/src/Catalyst.Modules.UPnP/PortMapperConstants.cs @@ -0,0 +1,53 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ +#endregion + +namespace Catalyst.Modules.UPnP +{ + public static class PortMapperConstants + { + public const string CouldNotCommunicateWithRouter = "Sorry, it wasn't possible to communicate with your router."; + public const string CouldNotCommunicateWithRouterException = "Sorry, it wasn't possible to communicate with your router, recieved error message: {0}"; + public const string NoExistingMapping = "There is no existing mapping for protocol = {0}, public port = {1}, private port = {2}."; + public const string DeletedMapping = + "Deleted the mapping for protocol = {0}, public port = {1}, private port = {2}."; + public const string CouldNotDeleteMapping = + "It wasn't possible to delete the mapping for protocol = {0}, public port = {1}, private port = {2}."; + public const string CouldNotCreateMapping = + "It wasn't possible to create a mapping for protocol = {0}, public port = {1}, private port = {2}."; + public const string CreatedMapping = + "Created a mapping for protocol = {0}, public port = {1}, private port = {2}."; + public const string ConflictingMappingExists = + "There is an existing mapping which conflicts with requested mapping protocol = {0}, public port = {1}, private port = {2}."; + public const string StoppedSearching = "Stopped searching for the router."; + public const string StartedSearching = "Started searching for a compatible router..."; + public const string DefaultUdpProperty = "CatalystNodeConfiguration.Rpc.Port"; + public const string DefaultTcpProperty = "CatalystNodeConfiguration.Peer.Port"; + + public enum Result + { + Timeout, + TaskFinished, + } + + public const int DefaultTimeout = 10; + } +} diff --git a/src/Catalyst.Modules.UPnP/PortMappingParser.cs b/src/Catalyst.Modules.UPnP/PortMappingParser.cs new file mode 100644 index 0000000000..d2671e8037 --- /dev/null +++ b/src/Catalyst.Modules.UPnP/PortMappingParser.cs @@ -0,0 +1,58 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ +#endregion + +using System.Collections.Generic; +using System.Linq; +using Mono.Nat; +using Newtonsoft.Json.Linq; + +namespace Catalyst.Modules.UPnP +{ + public static class PortMappingParser + { + public static List ParseJson(string tcp, string udp, string json) + { + List mappings = new(); + var jObject = JObject.Parse(json); + + mappings.AddRange(tcp.Split(',') + .Select(identifier => PortMappingFromJson(jObject, identifier, Protocol.Tcp)) + .Where(mapping => mapping != null) + .ToList()); + + mappings.AddRange(udp.Split(',') + .Select(identifier => PortMappingFromJson(jObject, identifier, Protocol.Udp)) + .Where(mapping => mapping != null) + .ToList()); + + return mappings; + } + + private static Mapping PortMappingFromJson(JToken jObject, string key, Protocol protocol) + { + var portToken = jObject.SelectToken(key); + var port = portToken?.ToObject(); + + return port!=null? new Mapping(protocol, port.Value, port.Value) : null; + } + } +} diff --git a/src/Catalyst.Modules.UPnP/Program.cs b/src/Catalyst.Modules.UPnP/Program.cs new file mode 100644 index 0000000000..ebd937207e --- /dev/null +++ b/src/Catalyst.Modules.UPnP/Program.cs @@ -0,0 +1,125 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ +#endregion + +using System; +using CommandLine; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Mono.Nat; +using Newtonsoft.Json; +using Serilog; + +namespace Catalyst.Modules.UPnP +{ + internal class Options + { + [Option('t', "timeout", HelpText = "The delay in seconds after which this application will stop.")] + public int Timeout { get; set; } + + [Option("filepath", Required = false, HelpText = "The path of the mapping file")] + public string FilePath { get; set; } + + [Option("tcp", Default = PortMapperConstants.DefaultTcpProperty, + HelpText = "The identifier of the tcp ports to be mapped; multiple identifiers can be provided using comma separators")] + public string TcpProperties { get; set; } + + [Option( "udp", Default = PortMapperConstants.DefaultUdpProperty, + HelpText = "The identifier of the udp ports to be mapped; multiple identifiers can be provided using comma separators")] + public string UdpProperties { get; set; } + + [Option( "delete", Default = false, HelpText = "To remove rather than add the specified port mappings")] + public bool IsMappingDeletion { get; set; } + } + + public static partial class Program + { + public static Task Main(string[] args) + { + var logger = new LoggerConfiguration() + .WriteTo.Console() + .CreateLogger(); + return Start(args, logger, new NatUtilityProvider()); + } + + public static async Task Start(string[] args, ILogger logger, INatUtilityProvider natUtilityProvider) + { + var result = await Parser.Default + .ParseArguments(args).MapResult(async options => await RunPortMapper(options, logger, natUtilityProvider), + response => Task.FromResult(1)).ConfigureAwait(false); + + return Environment.ExitCode = result; + } + + private static async Task RunPortMapper(Options options, ILogger logger, INatUtilityProvider provider) + { + var json = LoadFileWithLogging(options.FilePath, logger); + if (json==null) return 0; + + var mappings = ParseJsonWithLogging(options.TcpProperties, options.UdpProperties, json, logger); + if (!(mappings?.Count > 0)) return 0; + + PortMapper portMapper = new(provider, logger); + + var timeout = options.Timeout > 0 ? options.Timeout : PortMapperConstants.DefaultTimeout; + + await portMapper.MapPorts(mappings.ToArray(), new CancellationTokenSource(), timeout, options.IsMappingDeletion); + return 0; + } + + private static string LoadFileWithLogging(string path, ILogger logger) + { + try + { + using StreamReader r = new(path); + return r.ReadToEnd(); + } + catch(Exception) + { + logger.Error("Unable to find or read the file provided."); + } + return null; + } + + private static List ParseJsonWithLogging(string tcp, string udp, string json, ILogger logger) + { + List mappings; + try + { + mappings = PortMappingParser.ParseJson(tcp, udp, json); + } + catch(JsonReaderException) + { + logger.Error("The file did not contain valid json"); + return null; + } + + if (mappings?.Count>0) + { + return mappings; + } + logger.Error("No information in the file matched the parameters supplied."); + return null; + } + } +} diff --git a/src/Catalyst.Node.POA.CE.Tests/Catalyst.Node.POA.CE.Tests.csproj b/src/Catalyst.Node.POA.CE.Tests/Catalyst.Node.POA.CE.Tests.csproj index 238b56b97f..5fffd93c8b 100644 --- a/src/Catalyst.Node.POA.CE.Tests/Catalyst.Node.POA.CE.Tests.csproj +++ b/src/Catalyst.Node.POA.CE.Tests/Catalyst.Node.POA.CE.Tests.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.0 - James Kirby (nshcore@protonmail.com) + net6.0 + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Node.POA.CE.Tests.snk true @@ -9,17 +9,23 @@ Catalyst.Node.POA.CE.Tests - + + 1701;1702;VSTHRD200;CS8002 + - + - - - + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + diff --git a/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/Config/ConfigurableRepoIntegrationTests.cs b/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/Config/ConfigurableRepoIntegrationTests.cs deleted file mode 100644 index 81ed34c03d..0000000000 --- a/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/Config/ConfigurableRepoIntegrationTests.cs +++ /dev/null @@ -1,62 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System.IO; -using System.Threading.Tasks; -using Autofac; -using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.TestUtils; -using FluentAssertions; -using Xunit.Abstractions; - -namespace Catalyst.Node.POA.CE.Tests.IntegrationTests.Config -{ - public sealed class ConfigurableRepoIntegrationTests : FileSystemBasedTest - { - public ConfigurableRepoIntegrationTests(ITestOutputHelper output) : base(output) { } - - private async Task ModuleCanSaveAndRetrieveValuesFromRepository(FileInfo moduleFile) - { - using (var scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName + moduleFile)) - { - var component = scope.Resolve(); - - var guid = CorrelationId.GenerateCorrelationId().ToString(); - var storedItem = new StoredItem {Name = guid, Value = 10}; - - component.StringRepository.Add(storedItem); - - component.StringRepository.TryFind(s => s.Name == guid, out var retrieveItem); - - retrieveItem.Name.Should().Be(storedItem.Name); - retrieveItem.Value.Should().Be(storedItem.Value); - } - } - - protected override void Dispose(bool disposing) - { - base.Dispose(disposing); - if (!disposing) { } - } - } -} diff --git a/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/Config/GlobalConfigTests.cs b/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/Config/GlobalConfigTests.cs index 4a86d7b7c7..e0a09d87c9 100644 --- a/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/Config/GlobalConfigTests.cs +++ b/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/Config/GlobalConfigTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,12 +27,14 @@ using Autofac; using Catalyst.Abstractions; using Catalyst.Abstractions.Cli; +using Catalyst.Abstractions.Config; using Catalyst.Abstractions.DAO; using Catalyst.Abstractions.P2P.Discovery; using Catalyst.Core.Lib; using Catalyst.Core.Lib.Cli; using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Ledger; using Catalyst.Core.Modules.Authentication; using Catalyst.Core.Modules.Consensus; using Catalyst.Core.Modules.Cryptography.BulletProofs; @@ -42,15 +44,19 @@ using Catalyst.Core.Modules.Kvm; using Catalyst.Core.Modules.Ledger; using Catalyst.Core.Modules.Mempool; -using Catalyst.Core.Modules.Rpc.Server; +using Catalyst.Core.Modules.Sync; +using Catalyst.Modules.Network.LibP2P; using Catalyst.Protocol.Network; using Catalyst.TestUtils; +using Nethermind.Db; using NSubstitute; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; +using SharpRepository.InMemoryRepository; +using SharpRepository.Repository; namespace Catalyst.Node.POA.CE.Tests.IntegrationTests.Config { + [Parallelizable(ParallelScope.None)] public class GlobalConfigTests : FileSystemBasedTest { public static readonly List Networks = @@ -60,21 +66,28 @@ public class GlobalConfigTests : FileSystemBasedTest private IEnumerable _configFilesUsed; private ContainerProvider _containerProvider; - public GlobalConfigTests(ITestOutputHelper output) : base(output) { } + [SetUp] + public void Init() + { + this.Setup(TestContext.CurrentContext); + } - [Theory] - [MemberData(nameof(Networks))] + [TestCaseSource(nameof(Networks))] public void Registering_All_Configs_Should_Allow_Resolving_CatalystNode(NetworkType network) { _configFilesUsed = new[] { Constants.NetworkConfigFile(network), - Constants.SerilogJsonConfigFile + Constants.SerilogJsonConfigFile, + Constants.ValidatorSetConfigFile } .Select(f => Path.Combine(Constants.ConfigSubFolder, f)); _containerProvider = new ContainerProvider(_configFilesUsed, FileSystem, Output); + var networkTypeProvider = Substitute.For(); + networkTypeProvider.NetworkType.Returns(network); + SocketPortHelper.AlterConfigurationToGetUniquePort(_containerProvider.ConfigurationRoot); _containerProvider.ConfigureContainerBuilder(); @@ -84,17 +97,19 @@ public void Registering_All_Configs_Should_Allow_Resolving_CatalystNode(NetworkT containerBuilder.RegisterType().As(); containerBuilder.RegisterType().As(); containerBuilder.RegisterInstance(Substitute.For()).As(); + containerBuilder.RegisterInstance(networkTypeProvider).As(); + containerBuilder.RegisterModule(new KeystoreModule()); containerBuilder.RegisterModule(new KeySignerModule()); containerBuilder.RegisterModule(new ConsensusModule()); containerBuilder.RegisterModule(new DfsModule()); - containerBuilder.RegisterModule(new KvmModule()); containerBuilder.RegisterModule(new LedgerModule()); - containerBuilder.RegisterModule(new RpcServerModule()); containerBuilder.RegisterModule(new MempoolModule()); containerBuilder.RegisterModule(new KeystoreModule()); containerBuilder.RegisterModule(new BulletProofsModule()); containerBuilder.RegisterModule(new AuthenticationModule()); - + containerBuilder.RegisterModule(new SynchroniserModule()); + containerBuilder.RegisterModule(new LibP2PNetworkModule()); + containerBuilder.RegisterType>().As>().SingleInstance(); containerBuilder.RegisterAssemblyTypes(typeof(CoreLibProvider).Assembly) .AssignableTo().As(); containerBuilder.RegisterType().As() @@ -104,14 +119,17 @@ public void Registering_All_Configs_Should_Allow_Resolving_CatalystNode(NetworkT { _ = scope.Resolve(); } + + _containerProvider?.Dispose(); } protected override void Dispose(bool disposing) { base.Dispose(disposing); - if (!disposing) return; - - _containerProvider?.Dispose(); + if (!disposing) + { + return; + } } } } diff --git a/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/IO/FileSystemTest.cs b/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/IO/FileSystemTest.cs index 16cb2155c6..c8ad1c9206 100644 --- a/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/IO/FileSystemTest.cs +++ b/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/IO/FileSystemTest.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,8 +26,8 @@ using Catalyst.Protocol.Network; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; + namespace Catalyst.Node.POA.CE.Tests.IntegrationTests.IO { @@ -36,14 +36,17 @@ namespace Catalyst.Node.POA.CE.Tests.IntegrationTests.IO /// A base test class that can be used to offer inheriting tests a folder on which /// to create files, logs, etc. /// - [Trait(Traits.TestType, Traits.IntegrationTest)] + public sealed class FileSystemTest : FileSystemBasedTest { - private readonly FileSystem _fileSystem; - private readonly string _sourceFolder; + private FileSystem _fileSystem; + private string _sourceFolder; - public FileSystemTest(ITestOutputHelper output) : base(output) + [SetUp] + public void Init() { + this.Setup(TestContext.CurrentContext); + _fileSystem = new FileSystem(); _sourceFolder = Setup(); @@ -65,11 +68,10 @@ private bool CheckSavedPath(string path) .Equals(path.ToLower(CultureInfo.InvariantCulture)); } - [Theory] - [Trait(Traits.TestType, Traits.IntegrationTest)] - [InlineData("'\0'")] - [InlineData("'xxx://gan\0'dolf\\treasu\re*&+'")] - [InlineData("'q*Pen\0'cilL:\\123\\fak/e'")] + + [TestCase("'\0'")] + [TestCase("'xxx://gan\0'dolf\\treasu\re*&+'")] + [TestCase("'q*Pen\0'cilL:\\123\\fak/e'")] public void Save_Invalid_Data_Directory_Must_Fail(string path) { _fileSystem.SetCurrentPath(path).Should().BeFalse(); @@ -77,8 +79,7 @@ public void Save_Invalid_Data_Directory_Must_Fail(string path) CheckSavedPath(path).Should().BeFalse(); } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] public void Save_Existent_Data_Directory_Must_Succeed() { _fileSystem.SetCurrentPath(_sourceFolder).Should().BeTrue(); @@ -86,13 +87,13 @@ public void Save_Existent_Data_Directory_Must_Succeed() CheckSavedPath(_sourceFolder).Should().BeTrue(); } - [Fact(Skip = "TODO: Data directory logic has been removed")] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Ignore("TODO: Data directory logic has been removed")] + public void Save_Data_Directory_Several_Times_New_Instance_Must_Load_With_New_Data_Directory() { _fileSystem.SetCurrentPath(_sourceFolder).Should().BeTrue(); - var fileSystem = new FileSystem(); + FileSystem fileSystem = new(); CheckSavedPath(_sourceFolder).Should().BeTrue(); diff --git a/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/PoaConsensusTests.cs b/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/PoaConsensusTests.cs index 92a7b76bf5..7e73e1bea6 100644 --- a/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/PoaConsensusTests.cs +++ b/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/PoaConsensusTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,71 +28,86 @@ using System.Linq; using System.Threading; using System.Threading.Tasks; -using Autofac; +using Catalyst.Abstractions.FileSystem; using Catalyst.Core.Modules.Consensus.Cycle; using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NSubstitute; +using NUnit.Framework; namespace Catalyst.Node.POA.CE.Tests.IntegrationTests { + [TestFixture] + [Category(Traits.IntegrationTest)] public sealed class PoaConsensusTests : FileSystemBasedTest { - private readonly CancellationTokenSource _endOfTestCancellationSource; - private readonly ILifetimeScope _scope; - private readonly List _nodes; + private CancellationTokenSource _endOfTestCancellationSource; + private List _nodes; - public PoaConsensusTests(ITestOutputHelper output) : base(output) + [SetUp] + public void Init() { + this.Setup(TestContext.CurrentContext); + ContainerProvider.ConfigureContainerBuilder(true, true, true); - _scope = ContainerProvider.Container.BeginLifetimeScope(CurrentTestName); - var context = new FfiWrapper(); + FfiWrapper context = new(); _endOfTestCancellationSource = new CancellationTokenSource(); var poaNodeDetails = Enumerable.Range(0, 3).Select(i => - { - var privateKey = context.GeneratePrivateKey(); - var publicKey = privateKey.GetPublicKey(); - var nodeSettings = PeerSettingsHelper.TestPeerSettings(publicKey.Bytes, 2000 + i); - var peerIdentifier = nodeSettings.PeerId; - var name = $"producer{i.ToString()}"; - return new {index = i, name, privateKey, nodeSettings, peerIdentifier}; - } - ).ToList(); + { + var fileSystem = Substitute.For(); + var path = Path.Combine(FileSystem.GetCatalystDataDir().FullName, $"producer{i}"); + fileSystem.GetCatalystDataDir().Returns(new DirectoryInfo(path)); - var peerIdentifiers = poaNodeDetails.Select(n => n.peerIdentifier).ToList(); + return new { index = i, fileSystem }; + } + ).ToList(); - _nodes = poaNodeDetails.Select( - p => new PoaTestNode($"producer{p.index.ToString()}", - p.privateKey, - p.nodeSettings, - peerIdentifiers.Except(new[] {p.peerIdentifier}), - FileSystem, - output)).ToList(); + _nodes = new List(); + foreach (var nodeDetails in poaNodeDetails) + { + PoaTestNode node = new(nodeDetails.index, true, nodeDetails.fileSystem); + _nodes.Add(node); + } } - [Fact] + [Test] + [Ignore("To be fixed in issue #1241")] public async Task Run_ConsensusAsync() { _nodes.AsParallel() .ForAll(n => { + n.PeerActive += (peerAddress) => + { + _nodes.ForEach(async node => await node.RegisterPeerAddressAsync(peerAddress)); + }; + n?.RunAsync(_endOfTestCancellationSource.Token); - n?.Consensus.StartProducing(); }); await Task.Delay(Debugger.IsAttached ? TimeSpan.FromHours(3) - : CycleConfiguration.Default.CycleDuration.Multiply(1.3)) + : CycleConfiguration.Default.CycleDuration.Multiply(2.3)) .ConfigureAwait(false); - var dfsDir = Path.Combine(FileSystem.GetCatalystDataDir().FullName, "dfs"); - Directory.GetFiles(dfsDir).Length.Should().Be(1, - "only the elected producer should score high enough to see his block elected."); + //At least one delta should be produced + var maxDeltasProduced = 1; + List files = new(); + for (var i = 0; i < _nodes.Count; i++) + { + var dfsDir = Path.Combine(FileSystem.GetCatalystDataDir().FullName, $"producer{i}/dfs", "blocks"); + var deltaFiles = Directory.GetFiles(dfsDir).Select(x => new FileInfo(x).Name).ToList(); + maxDeltasProduced = Math.Max(maxDeltasProduced, deltaFiles.Count()); + files.AddRange(deltaFiles); + } + + files.Distinct().Count().Should().Be(maxDeltasProduced, + "only the elected producer should score high enough to see his block elected. Found: " + + files.Aggregate((x, y) => x + "," + y)); _endOfTestCancellationSource.CancelAfter(TimeSpan.FromMinutes(3)); } @@ -100,16 +115,6 @@ await Task.Delay(Debugger.IsAttached protected override void Dispose(bool disposing) { base.Dispose(disposing); - if (!disposing) return; - - if (_endOfTestCancellationSource.Token.IsCancellationRequested - && _endOfTestCancellationSource.Token.CanBeCanceled) - _endOfTestCancellationSource.Cancel(); - - _endOfTestCancellationSource.Dispose(); - _nodes.AsParallel().ForAll(n => n.Dispose()); - - _scope.Dispose(); } } } diff --git a/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/PoaTestNode.cs b/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/PoaTestNode.cs deleted file mode 100644 index ab93bf32be..0000000000 --- a/src/Catalyst.Node.POA.CE.Tests/IntegrationTests/PoaTestNode.cs +++ /dev/null @@ -1,177 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; -using Autofac; -using Catalyst.Abstractions; -using Catalyst.Abstractions.Consensus; -using Catalyst.Abstractions.Cryptography; -using Catalyst.Abstractions.Dfs; -using Catalyst.Abstractions.FileSystem; -using Catalyst.Abstractions.KeySigner; -using Catalyst.Abstractions.Keystore; -using Catalyst.Abstractions.Mempool; -using Catalyst.Abstractions.P2P; -using Catalyst.Abstractions.P2P.Discovery; -using Catalyst.Abstractions.Rpc; -using Catalyst.Abstractions.Types; -using Catalyst.Core.Lib.Config; -using Catalyst.Core.Lib.DAO; -using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.FileSystem; -using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; -using Catalyst.Core.Modules.Dfs; -using Catalyst.Core.Modules.Hashing; -using Catalyst.Core.Modules.Mempool; -using Catalyst.Core.Modules.Rpc.Server; -using Catalyst.Core.Modules.Web3; -using Catalyst.Protocol.Network; -using Catalyst.Protocol.Peer; -using Catalyst.TestUtils; -using NSubstitute; -using SharpRepository.InMemoryRepository; -using TheDotNetLeague.MultiFormats.MultiHash; -using Xunit.Abstractions; - -namespace Catalyst.Node.POA.CE.Tests.IntegrationTests -{ - public class PoaTestNode : ICatalystNode, IDisposable - { - private readonly DevDfs _dfs; - private readonly IMempool _memPool; - private readonly ICatalystNode _node; - private readonly DirectoryInfo _nodeDirectory; - private readonly PeerId _nodePeerId; - private readonly IPeerSettings _nodeSettings; - private readonly IPeerRepository _peerRepository; - private readonly IRpcServerSettings _rpcSettings; - private readonly ILifetimeScope _scope; - private readonly ContainerProvider _containerProvider; - - public PoaTestNode(string name, - IPrivateKey privateKey, - IPeerSettings nodeSettings, - IEnumerable knownPeerIds, - IFileSystem parentTestFileSystem, - ITestOutputHelper output) - { - Name = name; - _nodeSettings = nodeSettings; - - _nodeDirectory = parentTestFileSystem.GetCatalystDataDir().SubDirectoryInfo(Name); - var nodeFileSystem = Substitute.ForPartsOf(); - nodeFileSystem.GetCatalystDataDir().Returns(_nodeDirectory); - - _rpcSettings = RpcSettingsHelper.GetRpcServerSettings(nodeSettings.Port + 100); - _nodePeerId = nodeSettings.PeerId; - - var baseDfsFolder = Path.Combine(parentTestFileSystem.GetCatalystDataDir().FullName, "dfs"); - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - _dfs = new DevDfs(parentTestFileSystem, hashProvider, baseDfsFolder); - - _memPool = new Mempool( - new TestMempoolRepository(new InMemoryRepository())); - _peerRepository = Substitute.For(); - var peersInRepo = knownPeerIds.Select(p => new Peer - { - PeerId = p - }).ToList(); - _peerRepository.AsQueryable().Returns(peersInRepo.AsQueryable()); - _peerRepository.GetAll().Returns(peersInRepo); - _peerRepository.Get(Arg.Any()).Returns(ci => - { - return peersInRepo.First(p => p.DocumentId.Equals((string) ci[0])); - }); - - _containerProvider = new ContainerProvider(new[] - { - Constants.NetworkConfigFile(NetworkType.Devnet), - Constants.SerilogJsonConfigFile - } - .Select(f => Path.Combine(Constants.ConfigSubFolder, f)), parentTestFileSystem, output); - - Program.RegisterNodeDependencies(_containerProvider.ContainerBuilder, - excludedModules: new List {typeof(ApiModule), typeof(RpcServerModule)} - ); - _containerProvider.ConfigureContainerBuilder(true, true); - OverrideContainerBuilderRegistrations(); - - _scope = _containerProvider.Container.BeginLifetimeScope(Name); - _node = _scope.Resolve(); - - var keyStore = _scope.Resolve(); - var keyRegistry = _scope.Resolve(); - keyRegistry.AddItemToRegistry(KeyRegistryTypes.DefaultKey, privateKey); - - keyStore.KeyStoreEncryptAsync(privateKey, nodeSettings.NetworkType, KeyRegistryTypes.DefaultKey) - .ConfigureAwait(false).GetAwaiter() - .GetResult(); - } - - public string Name { get; } - - public IConsensus Consensus => _node.Consensus; - - public async Task RunAsync(CancellationToken cancellationSourceToken) - { - await _node.RunAsync(cancellationSourceToken).ConfigureAwait(false); - } - - public async Task StartSocketsAsync() { await _node.StartSocketsAsync(); } - - public void Dispose() { Dispose(true); } - - protected void OverrideContainerBuilderRegistrations() - { - _containerProvider.ContainerBuilder.RegisterInstance(new TestPasswordReader()).As(); - _containerProvider.ContainerBuilder.RegisterInstance(_nodeSettings).As(); - _containerProvider.ContainerBuilder.RegisterInstance(_rpcSettings).As(); - _containerProvider.ContainerBuilder.RegisterInstance(_nodePeerId).As(); - _containerProvider.ContainerBuilder.RegisterInstance(_dfs).As(); - _containerProvider.ContainerBuilder.RegisterInstance(_memPool).As>(); - _containerProvider.ContainerBuilder.RegisterInstance(_peerRepository).As(); - _containerProvider.ContainerBuilder.RegisterType().As() - .WithParameter("rootPath", _nodeDirectory.FullName); - _containerProvider.ContainerBuilder.RegisterInstance(Substitute.For()).As(); - var keySigner = Substitute.For(); - keySigner.Verify(Arg.Any(), Arg.Any(), default).ReturnsForAnyArgs(true); - keySigner.CryptoContext.SignatureLength.Returns(64); - _containerProvider.ContainerBuilder.RegisterInstance(keySigner).As(); - } - - protected virtual void Dispose(bool disposing) - { - if (!disposing) return; - - _scope?.Dispose(); - _peerRepository?.Dispose(); - _containerProvider?.Dispose(); - } - } -} diff --git a/src/Catalyst.Node.POA.CE.Tests/UnitTests/Config/ConfigCopierTests.cs b/src/Catalyst.Node.POA.CE.Tests/UnitTests/Config/ConfigCopierTests.cs index 40c9995244..b4e4ba5cb8 100644 --- a/src/Catalyst.Node.POA.CE.Tests/UnitTests/Config/ConfigCopierTests.cs +++ b/src/Catalyst.Node.POA.CE.Tests/UnitTests/Config/ConfigCopierTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,29 +30,32 @@ using Catalyst.Protocol.Network; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; + namespace Catalyst.Node.POA.CE.Tests.UnitTests.Config { public sealed class ConfigCopierTests : FileSystemBasedTest { - public ConfigCopierTests(ITestOutputHelper output) : base(output) { } - - private sealed class ConfigFilesOverwriteTestData : TheoryData + private sealed class ConfigFilesOverwriteTestData : List { public ConfigFilesOverwriteTestData() { - Add(Constants.NetworkConfigFile(NetworkType.Mainnet), NetworkType.Mainnet); - Add(Constants.NetworkConfigFile(NetworkType.Testnet), NetworkType.Testnet); - Add(Constants.NetworkConfigFile(NetworkType.Devnet), NetworkType.Devnet); - Add(Constants.SerilogJsonConfigFile, NetworkType.Devnet); + Add(new object[] { Constants.NetworkConfigFile(NetworkType.Mainnet), NetworkType.Mainnet }); + Add(new object[] { Constants.NetworkConfigFile(NetworkType.Testnet), NetworkType.Testnet }); + Add(new object[] { Constants.NetworkConfigFile(NetworkType.Devnet), NetworkType.Devnet }); + Add(new object[] { Constants.SerilogJsonConfigFile, NetworkType.Devnet }); } } - [Theory] - [ClassData(typeof(ConfigFilesOverwriteTestData))] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [SetUp] + public void Init() + { + this.Setup(TestContext.CurrentContext); + } + + [TestCaseSource(typeof(ConfigFilesOverwriteTestData))] + [Property(Traits.TestType, Traits.IntegrationTest)] public void RunConfigStartUp_Should_Not_Overwrite_An_Existing_Config_File(string moduleFileName, NetworkType network) { @@ -104,8 +107,8 @@ private static IEnumerable GetExpectedFileList(NetworkType network) return requiredConfigFiles; } - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] + [Property(Traits.TestType, Traits.IntegrationTest)] public void RunConfigStartUp_Should_Create_Folder_If_Needed() { var currentDirectory = FileSystem.GetCatalystDataDir(); @@ -119,7 +122,7 @@ public void RunConfigStartUp_Should_Create_Folder_If_Needed() configFiles.Should().BeEquivalentTo(expectedFileList); } - [Fact] + [Test] public async Task Can_Copy_Overridden_Network_File() { var overrideFile = "TestOverride.json"; diff --git a/src/Catalyst.Node.POA.CE.Tests/UnitTests/Config/NetworkConfigTests.cs b/src/Catalyst.Node.POA.CE.Tests/UnitTests/Config/NetworkConfigTests.cs index a86d1a9ae7..c959401bec 100644 --- a/src/Catalyst.Node.POA.CE.Tests/UnitTests/Config/NetworkConfigTests.cs +++ b/src/Catalyst.Node.POA.CE.Tests/UnitTests/Config/NetworkConfigTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,13 +27,19 @@ using System.Net; using Autofac; using Autofac.Configuration; +using Catalyst.Abstractions.Config; using Catalyst.Core.Lib.Config; using Catalyst.Core.Lib.P2P; using Catalyst.Protocol.Network; using FluentAssertions; using Microsoft.Extensions.Configuration; using SharpRepository.Repository; -using Xunit; +using NUnit.Framework; +using NSubstitute; +using Lib.P2P; +using Catalyst.Abstractions.Dfs.CoreApi; +using Newtonsoft.Json.Linq; +using MultiFormats; namespace Catalyst.Node.POA.CE.Tests.UnitTests.Config { @@ -43,15 +49,14 @@ public sealed class NetworkConfigTests static NetworkConfigTests() { - NetworkFiles = new List {NetworkType.Devnet, NetworkType.Mainnet, NetworkType.Testnet} + NetworkFiles = new List { NetworkType.Devnet, NetworkType.Mainnet, NetworkType.Testnet } .Select(n => new[] { Path.Combine(Constants.ConfigSubFolder, Constants.NetworkConfigFile(n)) as object }).ToList(); } - [Theory] - [MemberData(nameof(NetworkFiles))] + [TestCaseSource(nameof(NetworkFiles))] public void Network_Config_Should_Contain_a_valid_storage_module(string networkConfig) { var networkConfiguration = new ConfigurationBuilder().AddJsonFile(networkConfig).Build(); @@ -64,22 +69,34 @@ public void Network_Config_Should_Contain_a_valid_storage_module(string networkC persistenceConfiguration.DefaultRepository.Should().Be("inMemoryNoCaching"); } - [Theory] - [MemberData(nameof(NetworkFiles))] + [TestCaseSource(nameof(NetworkFiles))] public void Network_config_should_allow_building_PeerSettings(string networkConfig) { var configRoot = new ConfigurationBuilder().AddJsonFile(networkConfig).Build(); - var configModule = new ConfigurationModule(configRoot); + ConfigurationModule configModule = new(configRoot); - var containerBuilder = new ContainerBuilder(); + ContainerBuilder containerBuilder = new(); containerBuilder.RegisterModule(configModule); containerBuilder.RegisterInstance(configRoot).As(); - var peerSettings = new PeerSettings(configRoot); + MultiAddress address = new("/ip4/192.168.0.181/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"); + var peer = new Peer + { + PublicKey = "CAESLDAqMAUGAytlcAMhADyXIeZUUBKx3OiDdhDb5GGrDUPOhhzJWPf80Iqam3lr", + Addresses = new[] { address }, + Id = address.PeerId + }; + + var config = Substitute.For(); + var networkTypeProvider = Substitute.For(); + networkTypeProvider.NetworkType.Returns(NetworkType.Devnet); + var swarm = JToken.FromObject(new List { $"/ip4/0.0.0.0/tcp/4100" }); + config.GetAsync("Addresses.Swarm").Returns(swarm); + PeerSettings peerSettings = new(configRoot, peer, config, networkTypeProvider); peerSettings.Should().NotBeNull(); - peerSettings.NetworkType.Should().NotBeNull(); + peerSettings.NetworkType.Should().NotBe(null); peerSettings.Port.Should().BeInRange(1025, 65535); peerSettings.BindAddress.Should().BeOfType(); peerSettings.PublicKey.Should().NotBeNullOrWhiteSpace(); diff --git a/src/Catalyst.Node.POA.CE.sln b/src/Catalyst.Node.POA.CE.sln index 91d764cee4..9685a7a46e 100644 --- a/src/Catalyst.Node.POA.CE.sln +++ b/src/Catalyst.Node.POA.CE.sln @@ -1,37 +1,37 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29306.81 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 10.0.40219.1 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Node.POA.CE", "Catalyst.Node.POA.CE\Catalyst.Node.POA.CE.csproj", "{51EFC3B8-979D-4DAF-BD39-0127124B9B94}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Node.POA.CE.Tests", "Catalyst.Node.POA.CE.Tests\Catalyst.Node.POA.CE.Tests.csproj", "{5FB7FCBC-3848-4434-8E6F-D0C479FF9382}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Catalyst.Core", "Catalyst.Core", "{31CBF8EE-F057-4903-BD38-4DA20B39833F}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Catalyst.Framework", "Catalyst.Framework", "{31CBF8EE-F057-4903-BD38-4DA20B39833F}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Abstractions", "..\submodules\Catalyst.Node\src\Catalyst.Abstractions\Catalyst.Abstractions.csproj", "{01E07F92-FA02-4384-9014-F5AEF3C4E606}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Abstractions", "Catalyst.Abstractions\Catalyst.Abstractions.csproj", "{01E07F92-FA02-4384-9014-F5AEF3C4E606}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.TestUtils", "..\submodules\Catalyst.Node\src\Catalyst.TestUtils\Catalyst.TestUtils.csproj", "{8A29BD0D-4E17-4F74-91EC-78DE288F137B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.TestUtils", "Catalyst.TestUtils\Catalyst.TestUtils.csproj", "{8A29BD0D-4E17-4F74-91EC-78DE288F137B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nethermind", "nethermind", "{647A8E80-674D-454F-AAE9-3C321AAA4969}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Core", "..\submodules\Catalyst.Node\submodules\nethermind\src\Nethermind\Nethermind.Core\Nethermind.Core.csproj", "{319E059F-4CFA-4F0C-A6E7-4E8C037AA8E9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Core", "..\submodules\nethermind\src\Nethermind\Nethermind.Core\Nethermind.Core.csproj", "{319E059F-4CFA-4F0C-A6E7-4E8C037AA8E9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Store", "..\submodules\Catalyst.Node\submodules\nethermind\src\Nethermind\Nethermind.Store\Nethermind.Store.csproj", "{30745734-D408-4344-897A-341A3370B9C9}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Store", "..\submodules\nethermind\src\Nethermind\Nethermind.Store\Nethermind.Store.csproj", "{30745734-D408-4344-897A-341A3370B9C9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.HashLib", "..\submodules\Catalyst.Node\submodules\nethermind\src\Nethermind\Nethermind.HashLib\Nethermind.HashLib.csproj", "{835D62C9-659E-486F-ADEA-FD0F88682127}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.HashLib", "..\submodules\nethermind\src\Nethermind\Nethermind.HashLib\Nethermind.HashLib.csproj", "{835D62C9-659E-486F-ADEA-FD0F88682127}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Logging", "..\submodules\Catalyst.Node\submodules\nethermind\src\Nethermind\Nethermind.Logging\Nethermind.Logging.csproj", "{41A19F85-0ED8-4213-B6E9-CEA0BAF70E74}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Logging", "..\submodules\nethermind\src\Nethermind\Nethermind.Logging\Nethermind.Logging.csproj", "{41A19F85-0ED8-4213-B6E9-CEA0BAF70E74}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Secp256k1", "..\submodules\Catalyst.Node\submodules\nethermind\src\Nethermind\Nethermind.Secp256k1\Nethermind.Secp256k1.csproj", "{ADA7E764-5A1B-4E52-8299-6FD37D3015CC}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Secp256k1", "..\submodules\nethermind\src\Nethermind\Nethermind.Secp256k1\Nethermind.Secp256k1.csproj", "{ADA7E764-5A1B-4E52-8299-6FD37D3015CC}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Evm", "..\submodules\Catalyst.Node\submodules\nethermind\src\Nethermind\Nethermind.Evm\Nethermind.Evm.csproj", "{94976265-3C58-4617-B68B-E7F02B25B27B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Evm", "..\submodules\nethermind\src\Nethermind\Nethermind.Evm\Nethermind.Evm.csproj", "{94976265-3C58-4617-B68B-E7F02B25B27B}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Cli", "Catalyst.Cli\Catalyst.Cli.csproj", "{7710C659-28F6-4624-983B-B13109CCCD12}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Cli.Tests", "Catalyst.Cli.Tests\Catalyst.Cli.Tests.csproj", "{8FE5DCE4-E06E-4A2B-9CBC-A434E2B2FA0B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Dirichlet.Numerics", "..\submodules\Catalyst.Node\submodules\nethermind\src\Dirichlet\Nethermind.Dirichlet.Numerics\Nethermind.Dirichlet.Numerics.csproj", "{6201E2B9-4425-4B4F-A17C-853F3D75600C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Dirichlet.Numerics", "..\submodules\nethermind\src\Dirichlet\Nethermind.Dirichlet.Numerics\Nethermind.Dirichlet.Numerics.csproj", "{6201E2B9-4425-4B4F-A17C-853F3D75600C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionItems", "{7D13EA5F-6C87-402C-AAEF-82A3C8C66D9A}" ProjectSection(SolutionItems) = preProject @@ -48,43 +48,67 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "SolutionItems", "SolutionIt ..\SECURITY.md = ..\SECURITY.md EndProjectSection EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Protocol", "..\submodules\Catalyst.Node\src\Catalyst.Protocol\Catalyst.Protocol.csproj", "{F8847400-418A-470A-9E27-988990451481}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Protocol", "Catalyst.Protocol\Catalyst.Protocol.csproj", "{F8847400-418A-470A-9E27-988990451481}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Lib", "..\submodules\Catalyst.Node\src\Catalyst.Core.Lib\Catalyst.Core.Lib.csproj", "{433D58C3-E99F-4864-A660-D7AB1DB66F30}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Lib", "Catalyst.Core.Lib\Catalyst.Core.Lib.csproj", "{433D58C3-E99F-4864-A660-D7AB1DB66F30}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Authentication", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Authentication\Catalyst.Core.Modules.Authentication.csproj", "{9DBECEF8-7534-4BB8-A89C-F3196FC15FD5}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Authentication", "Catalyst.Core.Modules.Authentication\Catalyst.Core.Modules.Authentication.csproj", "{9DBECEF8-7534-4BB8-A89C-F3196FC15FD5}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Consensus", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Consensus\Catalyst.Core.Modules.Consensus.csproj", "{E3AE0FC0-AE86-437E-96EE-F0725A8B774B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Consensus", "Catalyst.Core.Modules.Consensus\Catalyst.Core.Modules.Consensus.csproj", "{E3AE0FC0-AE86-437E-96EE-F0725A8B774B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Cryptography.BulletProofs", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Cryptography.BulletProofs\Catalyst.Core.Modules.Cryptography.BulletProofs.csproj", "{8205DDC7-19E1-4215-AA4E-8EA864898098}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Cryptography.BulletProofs", "Catalyst.Core.Modules.Cryptography.BulletProofs\Catalyst.Core.Modules.Cryptography.BulletProofs.csproj", "{8205DDC7-19E1-4215-AA4E-8EA864898098}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Dfs", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Dfs\Catalyst.Core.Modules.Dfs.csproj", "{BCFB2216-F5E4-4D0A-8E41-01C055363723}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Dfs", "Catalyst.Core.Modules.Dfs\Catalyst.Core.Modules.Dfs.csproj", "{BCFB2216-F5E4-4D0A-8E41-01C055363723}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.KeySigner", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.KeySigner\Catalyst.Core.Modules.KeySigner.csproj", "{DFAC3518-3D42-4F90-8CE9-1638B6B2400C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.KeySigner", "Catalyst.Core.Modules.KeySigner\Catalyst.Core.Modules.KeySigner.csproj", "{DFAC3518-3D42-4F90-8CE9-1638B6B2400C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Keystore", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Keystore\Catalyst.Core.Modules.Keystore.csproj", "{2081AC8C-CC09-4C66-9D23-E4E8300BDB29}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Keystore", "Catalyst.Core.Modules.Keystore\Catalyst.Core.Modules.Keystore.csproj", "{2081AC8C-CC09-4C66-9D23-E4E8300BDB29}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Kvm", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Kvm\Catalyst.Core.Modules.Kvm.csproj", "{C02C7AED-277A-4BBE-8456-E1C487442C6B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Kvm", "Catalyst.Core.Modules.Kvm\Catalyst.Core.Modules.Kvm.csproj", "{C02C7AED-277A-4BBE-8456-E1C487442C6B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Ledger", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Ledger\Catalyst.Core.Modules.Ledger.csproj", "{A646BEAD-40CB-4B3D-84A5-96BA120AF47B}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Ledger", "Catalyst.Core.Modules.Ledger\Catalyst.Core.Modules.Ledger.csproj", "{A646BEAD-40CB-4B3D-84A5-96BA120AF47B}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Mempool", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Mempool\Catalyst.Core.Modules.Mempool.csproj", "{DD52F60D-2F43-49C7-A6A8-8C235317807C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Mempool", "Catalyst.Core.Modules.Mempool\Catalyst.Core.Modules.Mempool.csproj", "{DD52F60D-2F43-49C7-A6A8-8C235317807C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Client", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Rpc.Client\Catalyst.Core.Modules.Rpc.Client.csproj", "{0DE76285-45A9-4C3F-8472-70253711C2A4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Client", "Catalyst.Core.Modules.Rpc.Client\Catalyst.Core.Modules.Rpc.Client.csproj", "{0DE76285-45A9-4C3F-8472-70253711C2A4}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Server", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Rpc.Server\Catalyst.Core.Modules.Rpc.Server.csproj", "{FDF986C5-7FE3-419E-82D4-9F31134E3171}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Server", "Catalyst.Core.Modules.Rpc.Server\Catalyst.Core.Modules.Rpc.Server.csproj", "{FDF986C5-7FE3-419E-82D4-9F31134E3171}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Web3", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Web3\Catalyst.Core.Modules.Web3.csproj", "{CBE80931-25B7-44D5-A0C9-7A433BCEE03C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Web3", "Catalyst.Core.Modules.Web3\Catalyst.Core.Modules.Web3.csproj", "{CBE80931-25B7-44D5-A0C9-7A433BCEE03C}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Simulator", "..\submodules\Catalyst.Node\src\Catalyst.Simulator\Catalyst.Simulator.csproj", "{F67FC4C0-8C19-465B-ADF8-51B90F48AB74}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Simulator", "Catalyst.Simulator\Catalyst.Simulator.csproj", "{F67FC4C0-8C19-465B-ADF8-51B90F48AB74}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.POA.P2P.Discovery.Consortium", "..\submodules\Catalyst.Node\src\Catalyst.Modules.POA.P2P.Discovery.Consortium\Catalyst.Modules.POA.P2P.Discovery.Consortium.csproj", "{71ABE85C-2BEB-43B5-A1F4-6C8294CE83A4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.POA.P2P.Discovery.Consortium", "Catalyst.Modules.POA.P2P.Discovery.Consortium\Catalyst.Modules.POA.P2P.Discovery.Consortium.csproj", "{71ABE85C-2BEB-43B5-A1F4-6C8294CE83A4}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.POA.Consensus", "..\submodules\Catalyst.Node\src\Catalyst.Modules.POA.Consensus\Catalyst.Modules.POA.Consensus.csproj", "{6BECD71B-FBB7-433E-8F46-4B2233FC31E6}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.POA.Consensus", "Catalyst.Modules.POA.Consensus\Catalyst.Modules.POA.Consensus.csproj", "{6BECD71B-FBB7-433E-8F46-4B2233FC31E6}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.P2P.Discovery.Hastings", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.P2P.Discovery.Hastings\Catalyst.Core.Modules.P2P.Discovery.Hastings.csproj", "{C232BF7E-4DF2-4C81-99CF-17D529DF264C}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.P2P.Discovery.Hastings", "Catalyst.Core.Modules.P2P.Discovery.Hastings\Catalyst.Core.Modules.P2P.Discovery.Hastings.csproj", "{C232BF7E-4DF2-4C81-99CF-17D529DF264C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Catalyst.Core.Modules.Hashing", "..\submodules\Catalyst.Node\src\Catalyst.Core.Modules.Hashing\Catalyst.Core.Modules.Hashing.csproj", "{12A520A9-F736-4CC5-832B-230E11A7DFBB}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Hashing", "Catalyst.Core.Modules.Hashing\Catalyst.Core.Modules.Hashing.csproj", "{12A520A9-F736-4CC5-832B-230E11A7DFBB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.Repository.CosmosDb", "Catalyst.Modules.Repository.CosmosDb\Catalyst.Modules.Repository.CosmosDb.csproj", "{2A1AD069-FD87-48F2-B741-6DDF36BEAB81}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.Repository.MongoDb", "Catalyst.Modules.Repository.MongoDb\Catalyst.Modules.Repository.MongoDb.csproj", "{2557AA0F-B548-421B-851C-04BC7DB58305}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Consensus.AuRa", "..\submodules\nethermind\src\Nethermind\Nethermind.Consensus.AuRa\Nethermind.Consensus.AuRa.csproj", "{B6FC5C31-EB0A-4BD5-B051-7D6EC27331F6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Facade", "..\submodules\nethermind\src\Nethermind\Nethermind.Facade\Nethermind.Facade.csproj", "{8F103795-304C-43E0-846F-32F44487D892}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Wallet", "..\submodules\nethermind\src\Nethermind\Nethermind.Wallet\Nethermind.Wallet.csproj", "{245FE641-A0A9-46D5-8621-FF387A8A5E61}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Consensus", "..\submodules\nethermind\src\Nethermind\Nethermind.Consensus\Nethermind.Consensus.csproj", "{03373F7C-16BE-4F6D-9613-05DCEC86D292}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Consensus.Ethash", "..\submodules\nethermind\src\Nethermind\Nethermind.Consensus.Ethash\Nethermind.Consensus.Ethash.csproj", "{5D4030E5-378E-414C-BC51-C7A50942049E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Blockchain", "..\submodules\nethermind\src\Nethermind\Nethermind.Blockchain\Nethermind.Blockchain.csproj", "{884F4818-E169-4CD4-A540-9C19BE917338}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Store.Bloom", "..\submodules\nethermind\src\Nethermind\Nethermind.Store.Bloom\Nethermind.Store.Bloom.csproj", "{AEE1B7A6-D1B1-4451-8134-867BC038BEA5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.KeyStore", "..\submodules\nethermind\src\Nethermind\Nethermind.KeyStore\Nethermind.KeyStore.csproj", "{3387B03C-2B1D-437C-A9B2-371E8F14A25A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Network.Stats", "..\submodules\nethermind\src\Nethermind\Nethermind.Network.Stats\Nethermind.Network.Stats.csproj", "{7D957316-4B7D-4F8F-934A-E66E868F5820}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.TxPool", "..\submodules\nethermind\src\Nethermind\Nethermind.TxPool\Nethermind.TxPool.csproj", "{2E557AFD-3BDE-4B30-9925-55415EB0537E}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -220,6 +244,54 @@ Global {12A520A9-F736-4CC5-832B-230E11A7DFBB}.Debug|Any CPU.Build.0 = Debug|Any CPU {12A520A9-F736-4CC5-832B-230E11A7DFBB}.Release|Any CPU.ActiveCfg = Release|Any CPU {12A520A9-F736-4CC5-832B-230E11A7DFBB}.Release|Any CPU.Build.0 = Release|Any CPU + {2A1AD069-FD87-48F2-B741-6DDF36BEAB81}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2A1AD069-FD87-48F2-B741-6DDF36BEAB81}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2A1AD069-FD87-48F2-B741-6DDF36BEAB81}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2A1AD069-FD87-48F2-B741-6DDF36BEAB81}.Release|Any CPU.Build.0 = Release|Any CPU + {2557AA0F-B548-421B-851C-04BC7DB58305}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2557AA0F-B548-421B-851C-04BC7DB58305}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2557AA0F-B548-421B-851C-04BC7DB58305}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2557AA0F-B548-421B-851C-04BC7DB58305}.Release|Any CPU.Build.0 = Release|Any CPU + {B6FC5C31-EB0A-4BD5-B051-7D6EC27331F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B6FC5C31-EB0A-4BD5-B051-7D6EC27331F6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B6FC5C31-EB0A-4BD5-B051-7D6EC27331F6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B6FC5C31-EB0A-4BD5-B051-7D6EC27331F6}.Release|Any CPU.Build.0 = Release|Any CPU + {8F103795-304C-43E0-846F-32F44487D892}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8F103795-304C-43E0-846F-32F44487D892}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8F103795-304C-43E0-846F-32F44487D892}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8F103795-304C-43E0-846F-32F44487D892}.Release|Any CPU.Build.0 = Release|Any CPU + {245FE641-A0A9-46D5-8621-FF387A8A5E61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {245FE641-A0A9-46D5-8621-FF387A8A5E61}.Debug|Any CPU.Build.0 = Debug|Any CPU + {245FE641-A0A9-46D5-8621-FF387A8A5E61}.Release|Any CPU.ActiveCfg = Release|Any CPU + {245FE641-A0A9-46D5-8621-FF387A8A5E61}.Release|Any CPU.Build.0 = Release|Any CPU + {03373F7C-16BE-4F6D-9613-05DCEC86D292}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {03373F7C-16BE-4F6D-9613-05DCEC86D292}.Debug|Any CPU.Build.0 = Debug|Any CPU + {03373F7C-16BE-4F6D-9613-05DCEC86D292}.Release|Any CPU.ActiveCfg = Release|Any CPU + {03373F7C-16BE-4F6D-9613-05DCEC86D292}.Release|Any CPU.Build.0 = Release|Any CPU + {5D4030E5-378E-414C-BC51-C7A50942049E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D4030E5-378E-414C-BC51-C7A50942049E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D4030E5-378E-414C-BC51-C7A50942049E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D4030E5-378E-414C-BC51-C7A50942049E}.Release|Any CPU.Build.0 = Release|Any CPU + {884F4818-E169-4CD4-A540-9C19BE917338}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {884F4818-E169-4CD4-A540-9C19BE917338}.Debug|Any CPU.Build.0 = Debug|Any CPU + {884F4818-E169-4CD4-A540-9C19BE917338}.Release|Any CPU.ActiveCfg = Release|Any CPU + {884F4818-E169-4CD4-A540-9C19BE917338}.Release|Any CPU.Build.0 = Release|Any CPU + {AEE1B7A6-D1B1-4451-8134-867BC038BEA5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AEE1B7A6-D1B1-4451-8134-867BC038BEA5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AEE1B7A6-D1B1-4451-8134-867BC038BEA5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AEE1B7A6-D1B1-4451-8134-867BC038BEA5}.Release|Any CPU.Build.0 = Release|Any CPU + {3387B03C-2B1D-437C-A9B2-371E8F14A25A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {3387B03C-2B1D-437C-A9B2-371E8F14A25A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {3387B03C-2B1D-437C-A9B2-371E8F14A25A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {3387B03C-2B1D-437C-A9B2-371E8F14A25A}.Release|Any CPU.Build.0 = Release|Any CPU + {7D957316-4B7D-4F8F-934A-E66E868F5820}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7D957316-4B7D-4F8F-934A-E66E868F5820}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7D957316-4B7D-4F8F-934A-E66E868F5820}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7D957316-4B7D-4F8F-934A-E66E868F5820}.Release|Any CPU.Build.0 = Release|Any CPU + {2E557AFD-3BDE-4B30-9925-55415EB0537E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2E557AFD-3BDE-4B30-9925-55415EB0537E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2E557AFD-3BDE-4B30-9925-55415EB0537E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2E557AFD-3BDE-4B30-9925-55415EB0537E}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -254,6 +326,18 @@ Global {6BECD71B-FBB7-433E-8F46-4B2233FC31E6} = {31CBF8EE-F057-4903-BD38-4DA20B39833F} {C232BF7E-4DF2-4C81-99CF-17D529DF264C} = {31CBF8EE-F057-4903-BD38-4DA20B39833F} {12A520A9-F736-4CC5-832B-230E11A7DFBB} = {31CBF8EE-F057-4903-BD38-4DA20B39833F} + {2A1AD069-FD87-48F2-B741-6DDF36BEAB81} = {31CBF8EE-F057-4903-BD38-4DA20B39833F} + {2557AA0F-B548-421B-851C-04BC7DB58305} = {31CBF8EE-F057-4903-BD38-4DA20B39833F} + {B6FC5C31-EB0A-4BD5-B051-7D6EC27331F6} = {647A8E80-674D-454F-AAE9-3C321AAA4969} + {8F103795-304C-43E0-846F-32F44487D892} = {647A8E80-674D-454F-AAE9-3C321AAA4969} + {245FE641-A0A9-46D5-8621-FF387A8A5E61} = {647A8E80-674D-454F-AAE9-3C321AAA4969} + {03373F7C-16BE-4F6D-9613-05DCEC86D292} = {647A8E80-674D-454F-AAE9-3C321AAA4969} + {5D4030E5-378E-414C-BC51-C7A50942049E} = {647A8E80-674D-454F-AAE9-3C321AAA4969} + {884F4818-E169-4CD4-A540-9C19BE917338} = {647A8E80-674D-454F-AAE9-3C321AAA4969} + {AEE1B7A6-D1B1-4451-8134-867BC038BEA5} = {647A8E80-674D-454F-AAE9-3C321AAA4969} + {3387B03C-2B1D-437C-A9B2-371E8F14A25A} = {647A8E80-674D-454F-AAE9-3C321AAA4969} + {7D957316-4B7D-4F8F-934A-E66E868F5820} = {647A8E80-674D-454F-AAE9-3C321AAA4969} + {2E557AFD-3BDE-4B30-9925-55415EB0537E} = {647A8E80-674D-454F-AAE9-3C321AAA4969} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {FEAA105F-9B99-4CAE-8618-9214E3F691CB} diff --git a/src/Catalyst.Node.POA.CE/Catalyst.Node.POA.CE.csproj b/src/Catalyst.Node.POA.CE/Catalyst.Node.POA.CE.csproj index 03592b5c4a..59868237e7 100644 --- a/src/Catalyst.Node.POA.CE/Catalyst.Node.POA.CE.csproj +++ b/src/Catalyst.Node.POA.CE/Catalyst.Node.POA.CE.csproj @@ -1,10 +1,10 @@ - + - netcoreapp3.0 - Catalyst CLI - James Kirby (nshcore@protonmail.com) - CLI tool to interact with Catalyst Nodes via RPC - Copyright © 2019 AtlasCity.io + net6.0 + Catalyst POA Node + Darren Priestnall (darren.op@catalystnet.org) + Catalyst POA Node + Copyright © 2022 catalystnet.org Exe true Catalyst.Node.POA.CE.snk @@ -12,16 +12,18 @@ true Catalyst.Node.POA.CE + + 1701;1702;CS8002 + - + - - - + + @@ -32,8 +34,8 @@ - + diff --git a/src/Catalyst.Node.POA.CE/CatalystNodePoa.cs b/src/Catalyst.Node.POA.CE/CatalystNodePoa.cs index 3bad6d42b7..e3d266e3af 100644 --- a/src/Catalyst.Node.POA.CE/CatalystNodePoa.cs +++ b/src/Catalyst.Node.POA.CE/CatalystNodePoa.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,6 +22,7 @@ #endregion using System; +using System.Linq; using System.Threading; using System.Threading.Tasks; using Catalyst.Abstractions; @@ -30,54 +31,76 @@ using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.Dfs; using Catalyst.Abstractions.KeySigner; +using Catalyst.Abstractions.Keystore; using Catalyst.Abstractions.Ledger; using Catalyst.Abstractions.Mempool; using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Discovery; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Abstractions.Sync.Interfaces; using Catalyst.Abstractions.Types; -using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Transaction; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Modules.Kvm; +using Dawn; +using MultiFormats; using Serilog; -using TheDotNetLeague.MultiFormats.MultiBase; namespace Catalyst.Node.POA.CE { - public class CatalystNodePoa : ICatalystNode + public sealed class CatalystNodePoa : ICatalystNode { public IConsensus Consensus { get; } private readonly IContract _contract; - private readonly IDfs _dfs; + private readonly IDfsService _dfsService; private readonly ILedger _ledger; private readonly IKeySigner _keySigner; private readonly ILogger _logger; - private readonly IMempool _memPool; + private readonly IMempool _memPool; private readonly IPeerService _peer; private readonly IPeerClient _peerClient; private readonly IPeerSettings _peerSettings; private readonly IPublicKey _publicKey; + private readonly ISynchroniser _synchronizer; + private readonly IPeerRepository _peerRepository; + private readonly IKeyApi _keyApi; + private readonly IPeerDiscovery _peerDiscovery; public CatalystNodePoa(IKeySigner keySigner, IPeerService peer, IConsensus consensus, - IDfs dfs, + IDfsService dfsService, ILedger ledger, ILogger logger, IPeerClient peerClient, IPeerSettings peerSettings, - IMempool memPool, + IMempool memPool, + ISynchroniser synchronizer, + IPeerRepository peerRepository, + IKeyApi keyApi, + IPeerDiscovery peerDiscovery, IContract contract = null) { + Guard.Argument(peerRepository, nameof(peerRepository)).NotNull(); + _peer = peer; _peerClient = peerClient; _peerSettings = peerSettings; Consensus = consensus; - _dfs = dfs; + _dfsService = dfsService; _ledger = ledger; _keySigner = keySigner; _logger = logger; _memPool = memPool; _contract = contract; + _synchronizer = synchronizer; + _peerRepository = peerRepository; + _keyApi = keyApi; + _peerDiscovery = peerDiscovery; - var privateKey = keySigner.KeyStore.KeyStoreDecrypt(KeyRegistryTypes.DefaultKey); + var privateKey = keySigner.GetPrivateKey(KeyRegistryTypes.DefaultKey); _publicKey = keySigner.CryptoContext.GetPublicKeyFromPrivateKey(privateKey); + } public async Task StartSocketsAsync() @@ -89,10 +112,25 @@ public async Task StartSocketsAsync() public async Task RunAsync(CancellationToken ct) { _logger.Information("Starting the Catalyst Node"); - _logger.Information($"***** using PublicKey: {_publicKey.Bytes.ToBase32()} *****"); + var key = await _keyApi.GetKeyAsync("self").ConfigureAwait(false); + var peerId = key.Id; + + _logger.Information($"***** using PeerId: {peerId} *****"); + _logger.Information($"***** using PublicKey: {_publicKey.Bytes.ToBase58()} *****"); + _logger.Information($"***** using KvmAddress: {_publicKey.ToKvmAddress()} *****"); + + await _peerDiscovery?.DiscoveryAsync(); + + await _dfsService.StartAsync().ConfigureAwait(false); await StartSocketsAsync().ConfigureAwait(false); - Consensus.StartProducing(); + + _synchronizer.StartAsync().ConfigureAwait(false); + + _synchronizer.SyncCompleted.Subscribe((x) => + { + Consensus.StartProducing(); + }); bool exit; @@ -102,7 +140,7 @@ public async Task RunAsync(CancellationToken ct) _logger.Debug("Type 'exit' to exit, anything else to continue"); exit = string.Equals(Console.ReadLine(), "exit", StringComparison.OrdinalIgnoreCase); - } while (!ct.IsCancellationRequested && !exit); + } while (!ct.IsCancellationRequested); _logger.Debug("Stopping the Catalyst Node"); } diff --git a/src/Catalyst.Node.POA.CE/Config/devnet.json b/src/Catalyst.Node.POA.CE/Config/devnet.json index a2bdb56cd6..e1fdb03fd8 100644 --- a/src/Catalyst.Node.POA.CE/Config/devnet.json +++ b/src/Catalyst.Node.POA.CE/Config/devnet.json @@ -1,8 +1,6 @@ { "CatalystNodeConfiguration": { "Peer": { - "Network": "Devnet", - "PublicKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "Port": 42076, "BindAddress": "127.0.0.1", "PublicIpAddress": "127.0.0.1", diff --git a/src/Catalyst.Node.POA.CE/Config/mainnet.json b/src/Catalyst.Node.POA.CE/Config/mainnet.json index 82033085cd..303e34db03 100644 --- a/src/Catalyst.Node.POA.CE/Config/mainnet.json +++ b/src/Catalyst.Node.POA.CE/Config/mainnet.json @@ -1,7 +1,6 @@ { "CatalystNodeConfiguration": { "Peer": { - "Network": "Mainnet", "PublicKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "Port": 42076, "BindAddress": "127.0.0.1", diff --git a/src/Catalyst.Node.POA.CE/Config/poa.nodes.json b/src/Catalyst.Node.POA.CE/Config/poa.nodes.json index 18b7fada6c..7053088f58 100644 --- a/src/Catalyst.Node.POA.CE/Config/poa.nodes.json +++ b/src/Catalyst.Node.POA.CE/Config/poa.nodes.json @@ -1,7 +1,4 @@ [ - { - "Ip": "127.0.0.1", - "Port": 42076, - "PublicKey": "" - } + { "Address": "/ip4/77.68.110.78/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZe3GsQiDaWiuhi4ngsz4yYghzBZanWyrCCxbg3QWZmWCK3" }, + { "Address": "/ip4/77.68.110.23/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZdzcCxXYmaTejTEAM7waR2tok89tNzBzPFutQhjnSwtsAd" } ] diff --git a/src/Catalyst.Node.POA.CE/Config/serilog.json b/src/Catalyst.Node.POA.CE/Config/serilog.json index e845cb5b36..79fdd05b19 100644 --- a/src/Catalyst.Node.POA.CE/Config/serilog.json +++ b/src/Catalyst.Node.POA.CE/Config/serilog.json @@ -1,17 +1,17 @@ { - "Serilog": { - "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], - "MinimumLevel": "Verbose", - "WriteTo": [ - { - "Name": "Console", - "Args": { "outputTemplate": "{Timestamp:HH:mm:ss} [{Level:u3}] ({ThreadId}) {Message} ({SourceContext}){NewLine}{Exception}" } - }, - { "Name": "File" } - ], - "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId", "SourceContext" ], - "Properties": { - "Application": "Catalyst.Node.POA.CE" + "Serilog": { + "Using": [ "Serilog.Sinks.Console", "Serilog.Sinks.File" ], + "MinimumLevel": "Verbose", + "WriteTo": [ + { + "Name": "Console", + "Args": { "outputTemplate": "{Timestamp:HH:mm:ss} [{Level:u3}] ({ThreadId}) {Message} ({SourceContext}){NewLine}{Exception}" } + }, + { "Name": "File" } + ], + "Enrich": [ "FromLogContext", "WithMachineName", "WithThreadId", "SourceContext" ], + "Properties": { + "Application": "Catalyst.Node.POA.CE" + } } - } } diff --git a/src/Catalyst.Node.POA.CE/Config/testnet.json b/src/Catalyst.Node.POA.CE/Config/testnet.json index 6ebaa255de..303e34db03 100644 --- a/src/Catalyst.Node.POA.CE/Config/testnet.json +++ b/src/Catalyst.Node.POA.CE/Config/testnet.json @@ -1,7 +1,6 @@ { "CatalystNodeConfiguration": { "Peer": { - "Network": "Testnet", "PublicKey": "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa", "Port": 42076, "BindAddress": "127.0.0.1", diff --git a/src/Catalyst.Node.POA.CE/Config/validators.json b/src/Catalyst.Node.POA.CE/Config/validators.json new file mode 100644 index 0000000000..f5660b2c66 --- /dev/null +++ b/src/Catalyst.Node.POA.CE/Config/validators.json @@ -0,0 +1,12 @@ +{ + "validators": { + "multi": { + "0": { + "list": [ "0x5fe351dd36e699b1c87b48199a1739d4939fdcbe"] + }, + "900": { + "contract": "0x2932E7Af4fB3936c62eEff34Bf9c4448f1C2E63c" + } + } + } +} diff --git a/src/Catalyst.Node.POA.CE/PoaConfigCopier.cs b/src/Catalyst.Node.POA.CE/PoaConfigCopier.cs index 8112361f21..e6c807c826 100644 --- a/src/Catalyst.Node.POA.CE/PoaConfigCopier.cs +++ b/src/Catalyst.Node.POA.CE/PoaConfigCopier.cs @@ -1,7 +1,7 @@ -#region LICENSE +#region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -35,6 +35,7 @@ protected override IEnumerable RequiredConfigFiles(NetworkType network, return new List { PoaConstants.RpcAuthenticationCredentialsFile, + Constants.ValidatorSetConfigFile, Constants.SerilogJsonConfigFile, Constants.NetworkConfigFile(network, overrideNetworkFile) }; diff --git a/src/Catalyst.Node.POA.CE/PoaConstants.cs b/src/Catalyst.Node.POA.CE/PoaConstants.cs index 939d23ce33..e510891446 100644 --- a/src/Catalyst.Node.POA.CE/PoaConstants.cs +++ b/src/Catalyst.Node.POA.CE/PoaConstants.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Node.POA.CE/Program.cs b/src/Catalyst.Node.POA.CE/Program.cs index 5387a62366..f6aed119dc 100644 --- a/src/Catalyst.Node.POA.CE/Program.cs +++ b/src/Catalyst.Node.POA.CE/Program.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,12 +21,6 @@ #endregion -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Linq; -using System.Threading; -using System.Threading.Tasks; using Autofac; using Autofac.Core; using Catalyst.Abstractions; @@ -49,20 +43,26 @@ using Catalyst.Core.Modules.Ledger; using Catalyst.Core.Modules.Mempool; using Catalyst.Core.Modules.P2P.Discovery.Hastings; -using Catalyst.Core.Modules.Rpc.Server; +using Catalyst.Core.Modules.Sync; using Catalyst.Core.Modules.Web3; +using Catalyst.Core.Modules.Web3.Options; +using Catalyst.Modules.Network.LibP2P; using Catalyst.Modules.POA.Consensus; using Catalyst.Modules.POA.P2P; using Catalyst.Protocol.Network; using CommandLine; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; namespace Catalyst.Node.POA.CE { internal class Options { - [Option("ipfs-password", HelpText = "The password for IPFS. Defaults to prompting for the password.")] - public string IpfsPassword { get; set; } - [Option("ssl-cert-password", HelpText = "The password for ssl cert. Defaults to prompting for the password.")] public string SslCertPassword { get; set; } @@ -72,8 +72,18 @@ internal class Options [Option('o', "overwrite-config", HelpText = "Overwrite the data directory configs.")] public bool OverwriteConfig { get; set; } + [Option( "network-type", HelpText = "The network type")] + public NetworkType NetworkType { get; set; } + [Option("network-file", HelpText = "The name of the network file")] public string OverrideNetworkFile { get; set; } + + + [Option('r', "reset", HelpText = "Reset the state")] + public bool Reset { get; set; } + + [Option('u', "uninstall", HelpText = "Uninstall the node after execution")] + public bool Uninstall { get; set; } } public static class Program @@ -93,16 +103,11 @@ static Program() /// /// /// - private static async Task CustomBootLogic(Kernel kernel) + private static async Task CustomBootLogicAsync(Kernel kernel) { RegisterNodeDependencies(Kernel.ContainerBuilder); - kernel.StartContainer(); - kernel.Instance.Resolve() - .RunAsync(new CancellationToken()) - - // ReSharper disable once VSTHRD002 - .Wait(); + await kernel.Instance.Resolve().RunAsync(new CancellationToken()); } private static readonly Dictionary> DefaultModulesByTypes = @@ -111,11 +116,11 @@ private static async Task CustomBootLogic(Kernel kernel) {typeof(CoreLibProvider), () => new CoreLibProvider()}, {typeof(MempoolModule), () => new MempoolModule()}, {typeof(ConsensusModule), () => new ConsensusModule()}, + {typeof(SynchroniserModule), () => new SynchroniserModule()}, {typeof(KvmModule), () => new KvmModule()}, {typeof(LedgerModule), () => new LedgerModule()}, {typeof(HashingModule), () => new HashingModule()}, {typeof(DiscoveryHastingModule), () => new DiscoveryHastingModule()}, - {typeof(RpcServerModule), () => new RpcServerModule()}, {typeof(BulletProofsModule), () => new BulletProofsModule()}, {typeof(KeystoreModule), () => new KeystoreModule()}, {typeof(KeySignerModule), () => new KeySignerModule()}, @@ -123,10 +128,11 @@ private static async Task CustomBootLogic(Kernel kernel) {typeof(AuthenticationModule), () => new AuthenticationModule()}, { typeof(ApiModule), - () => new ApiModule("http://*:5005", new List {"Catalyst.Core.Modules.Web3"}) + () => new ApiModule(new HttpOptions(new IPEndPoint(IPAddress.Any, 5005)), new HttpsOptions(new IPEndPoint(IPAddress.Any, 2053), "cert.pfx"), new List {"Catalyst.Core.Modules.Web3", "Catalyst.Core.Modules.Dfs"}) }, {typeof(PoaConsensusModule), () => new PoaConsensusModule()}, {typeof(PoaP2PModule), () => new PoaP2PModule()}, + {typeof(LibP2PNetworkModule), () => new LibP2PNetworkModule()} }; public static void RegisterNodeDependencies(ContainerBuilder containerBuilder, @@ -142,10 +148,6 @@ public static void RegisterNodeDependencies(ContainerBuilder containerBuilder, containerBuilder.RegisterAssemblyTypes(typeof(CoreLibProvider).Assembly) .AssignableTo().As(); - containerBuilder.RegisterAssemblyTypes(typeof(RpcServerModule).Assembly) - .AssignableTo().As() - .PublicOnly(); - // DAO MapperInitialisers containerBuilder.RegisterAssemblyTypes(typeof(CoreLibProvider).Assembly) .AssignableTo().As(); @@ -157,21 +159,30 @@ public static void RegisterNodeDependencies(ContainerBuilder containerBuilder, .Select(p => p.Value()) .Concat(extraModuleInstances ?? new List()); - foreach (var module in modulesToRegister) containerBuilder.RegisterModule(module); + foreach (var module in modulesToRegister) + { + containerBuilder.RegisterModule(module); + } } - public static int Main(string[] args) + public static async Task Main(string[] args) { // Parse the arguments. - Parser.Default + var result = await Parser.Default .ParseArguments(args) - .WithParsed(async o => await RunAsync(o).ConfigureAwait(false)); + .MapResult(async options => await RunAsync(options).ConfigureAwait(false), + response => Task.FromResult(1)).ConfigureAwait(false); - return Environment.ExitCode; + return Environment.ExitCode = result; } - private static async Task RunAsync(Options options) + private static async Task RunAsync(Options options) { + // uncomment to speed up node launching + // options.IpfsPassword ??= "ipfs"; + // options.NodePassword ??= "node"; + // options.SslCertPassword ??= "cert"; + Kernel.Logger.Information("Catalyst.Node started with process id {0}", Process.GetCurrentProcess().Id.ToString()); @@ -179,22 +190,25 @@ private static async Task RunAsync(Options options) { await Kernel .WithDataDirectory() - .WithNetworksConfigFile(NetworkType.Devnet, options.OverrideNetworkFile) + .WithNetworkType(options.NetworkType) + .WithNetworksConfigFile(options.OverrideNetworkFile) .WithSerilogConfigFile() + .WithValidatorSetFile() .WithConfigCopier(new PoaConfigCopier()) .WithPersistenceConfiguration() .BuildKernel(options.OverwriteConfig) .WithPassword(PasswordRegistryTypes.DefaultNodePassword, options.NodePassword) - .WithPassword(PasswordRegistryTypes.IpfsPassword, options.IpfsPassword) .WithPassword(PasswordRegistryTypes.CertificatePassword, options.SslCertPassword) - .StartCustomAsync(CustomBootLogic); + .Reset(options.Reset) + .Uninstall(options.Uninstall) + .StartCustomAsync(CustomBootLogicAsync); - Environment.ExitCode = 0; + return 0; } catch (Exception e) { Kernel.Logger.Fatal(e, "Catalyst.Node stopped unexpectedly"); - Environment.ExitCode = 1; + return 1; } } } diff --git a/src/Catalyst.Protocol.Tests/Account/AddressTests.cs b/src/Catalyst.Protocol.Tests/Account/AddressTests.cs index bf064bef52..5a1036bea6 100644 --- a/src/Catalyst.Protocol.Tests/Account/AddressTests.cs +++ b/src/Catalyst.Protocol.Tests/Account/AddressTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,18 +29,14 @@ using Catalyst.Protocol.Account; using Catalyst.TestUtils.Protocol; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; + namespace Catalyst.Protocol.Tests.Account { public class AddressTests { - private readonly ITestOutputHelper _output; - - public AddressTests(ITestOutputHelper output) { _output = output; } - - [Fact] + [Test] public void Address_should_produce_different_bytes_for_all_possible_network_and_account_types() { var cartesianProduct = AddressHelper.GetAllNetworksAndAccountTypesCombinations(); @@ -48,7 +44,7 @@ public void Address_should_produce_different_bytes_for_all_possible_network_and_ var addressTypes = cartesianProduct.ToList(); var forOutput = addressTypes.Select(x => $"{Convert.ToString((int) x.NetworkType | (int) x.AccountType, 2).PadLeft(6, '0')} => {x.NetworkType}|{x.AccountType}"); - forOutput.ToList().ForEach(o => _output.WriteLine(o)); + forOutput.ToList().ForEach(o => TestContext.WriteLine(o)); var pubKeyBytes = ByteUtil.GenerateRandomByteArray(new FfiWrapper().PublicKeyLength); diff --git a/src/Catalyst.Protocol.Tests/Catalyst.Protocol.Tests.csproj b/src/Catalyst.Protocol.Tests/Catalyst.Protocol.Tests.csproj index 6ff1c3bad2..f0bccf25c8 100644 --- a/src/Catalyst.Protocol.Tests/Catalyst.Protocol.Tests.csproj +++ b/src/Catalyst.Protocol.Tests/Catalyst.Protocol.Tests.csproj @@ -1,28 +1,34 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Protocol.Tests - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Protocol.Tests.snk true - - - - - - - - - - - - - - - - - - + + 1701;1702;VSTHRD200;CS8002 + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + + diff --git a/src/Catalyst.Protocol.Tests/UnitTests/Deltas/DeltaTests.cs b/src/Catalyst.Protocol.Tests/UnitTests/Deltas/DeltaTests.cs index c6c6d59bd6..b4ec12eb3f 100644 --- a/src/Catalyst.Protocol.Tests/UnitTests/Deltas/DeltaTests.cs +++ b/src/Catalyst.Protocol.Tests/UnitTests/Deltas/DeltaTests.cs @@ -1,7 +1,7 @@ -#region LICENSE +#region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,22 +22,23 @@ #endregion using System; +using System.Collections.Generic; using System.IO; using Catalyst.Protocol.Deltas; using FluentAssertions; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; -using Xunit; +using NUnit.Framework; namespace Catalyst.Protocol.Tests.UnitTests.Deltas { public sealed class DeltaTests { - private sealed class InvalidDeltas : TheoryData + private sealed class InvalidDeltas : List { public InvalidDeltas() { - AddRow(new Delta + Add(new Delta { PreviousDeltaDfsHash = ByteString.Empty, MerkleRoot = ByteString.CopyFromUtf8("abc"), @@ -46,7 +47,7 @@ public InvalidDeltas() Nanos = 21, Seconds = 30 } }); - AddRow(new Delta + Add(new Delta { PreviousDeltaDfsHash = ByteString.CopyFromUtf8("abc"), MerkleRoot = ByteString.Empty, @@ -55,7 +56,7 @@ public InvalidDeltas() Nanos = 21, Seconds = 30 } }); - AddRow(new Delta + Add(new Delta { PreviousDeltaDfsHash = ByteString.CopyFromUtf8("abc"), MerkleRoot = ByteString.CopyFromUtf8("def"), @@ -64,14 +65,13 @@ public InvalidDeltas() } } - [Theory] - [ClassData(typeof(InvalidDeltas))] + [TestCaseSource(typeof(InvalidDeltas))] public void Delta_IsValid_Should_Throw_On_Invalid_Delta(Delta delta) { new Action(() => delta.IsValid()).Should().Throw(); } - [Fact] + [Test] public void Delta_IsValid_Should_Not_Throw_On_Valid_Delta() { var delta = new Delta diff --git a/src/Catalyst.Protocol.Tests/UnitTests/Extensions/ByteStringExtensionsTests.cs b/src/Catalyst.Protocol.Tests/UnitTests/Extensions/ByteStringExtensionsTests.cs index d7181eec16..c0aaca056a 100644 --- a/src/Catalyst.Protocol.Tests/UnitTests/Extensions/ByteStringExtensionsTests.cs +++ b/src/Catalyst.Protocol.Tests/UnitTests/Extensions/ByteStringExtensionsTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,18 +25,18 @@ using Catalyst.Core.Lib.Util; using FluentAssertions; using Google.Protobuf; -using Xunit; +using NUnit.Framework; namespace Catalyst.Protocol.Tests.UnitTests.Extensions { public sealed class ByteStringExtensionsTests { [Theory] - [InlineData(2)] - [InlineData(10)] - [InlineData(100)] - [InlineData(10000)] - [InlineData(5000000)] + [TestCase(2)] + [TestCase(10)] + [TestCase(100)] + [TestCase(10000)] + [TestCase(5000000)] public void Convert_ByteArray_To_ByteString_Should_Succeed(int arraySize) { var testBytes = ByteUtil.GenerateRandomByteArray(arraySize); @@ -44,7 +44,7 @@ public void Convert_ByteArray_To_ByteString_Should_Succeed(int arraySize) testBytes.ToByteString().Should().Equal(ByteString.CopyFrom(testBytes)); } - [Fact] + [Test] public void Convert_ByteArray_To_ByteString_Should_Fail() { var testBytes = new byte[500]; diff --git a/src/Catalyst.Protocol.Tests/UnitTests/ProtobuffExtensionsTests.cs b/src/Catalyst.Protocol.Tests/UnitTests/ProtobuffExtensionsTests.cs index 8d37d8a507..24033b0117 100644 --- a/src/Catalyst.Protocol.Tests/UnitTests/ProtobuffExtensionsTests.cs +++ b/src/Catalyst.Protocol.Tests/UnitTests/ProtobuffExtensionsTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -26,13 +26,13 @@ using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using FluentAssertions; -using Xunit; +using NUnit.Framework; namespace Catalyst.Protocol.Tests.UnitTests { public sealed class ProtobufExtensionsTests { - [Fact] + [Test] public void Can_Identify_Broadcast_Message() { var message = new TransactionBroadcast() @@ -42,14 +42,14 @@ public void Can_Identify_Broadcast_Message() message.IsBroadCastMessage().Should().BeTrue(); } - [Fact] + [Test] public void ShortenedFullName_should_remove_namespace_start() { TransactionBroadcast.Descriptor.FullName.Should().Be("Catalyst.Protocol.Wire.TransactionBroadcast"); TransactionBroadcast.Descriptor.ShortenedFullName().Should().Be("Wire.TransactionBroadcast"); } - [Fact] + [Test] public void ShortenedProtoFullName_should_remove_namespace_start() { PingRequest.Descriptor.FullName.Should().Be("Catalyst.Protocol.IPPN.PingRequest"); @@ -57,18 +57,18 @@ public void ShortenedProtoFullName_should_remove_namespace_start() } [Theory] - [InlineData("MyFunnyRequest", "MyFunnyResponse")] - [InlineData("Request", "Response")] - [InlineData("Some.Namespace.ClassRequest", "Some.Namespace.ClassResponse")] + [TestCase("MyFunnyRequest", "MyFunnyResponse")] + [TestCase("Request", "Response")] + [TestCase("Some.Namespace.ClassRequest", "Some.Namespace.ClassResponse")] public void GetResponseType_should_swap_request_suffix_for_response_suffix(string requestType, string responseType) { requestType.GetResponseType().Should().Be(responseType); } [Theory] - [InlineData("MyFunnyResponse", "MyFunnyRequest")] - [InlineData("Response", "Request")] - [InlineData("Some.Namespace.ClassResponse", "Some.Namespace.ClassRequest")] + [TestCase("MyFunnyResponse", "MyFunnyRequest")] + [TestCase("Response", "Request")] + [TestCase("Some.Namespace.ClassResponse", "Some.Namespace.ClassRequest")] public void GetRequestType_should_swap_request_suffix_for_response_suffix(string responseType, string requestType) { responseType.GetRequestType().Should().Be(requestType); diff --git a/src/Catalyst.Protocol.Tests/UnitTests/Shared/AddressTests.cs b/src/Catalyst.Protocol.Tests/UnitTests/Shared/AddressTests.cs index 3d35a32dbd..ce78b4b510 100644 --- a/src/Catalyst.Protocol.Tests/UnitTests/Shared/AddressTests.cs +++ b/src/Catalyst.Protocol.Tests/UnitTests/Shared/AddressTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,7 @@ // using FluentAssertions; // using Multiformats.Hash.Algorithms; // using NSubstitute; -// using Xunit; +// using NUnit.Framework; // // namespace Catalyst.Protocol.Tests.Shared // { @@ -42,13 +42,13 @@ // random.NextBytes(_noPrefixBytes); // } // -// [Fact] +// [Test] // public void Address_Constructor_From_Raw_Bytes_Should_Throw_On_Null_Argument() // { // new Action(() => new Address(null)).Should().Throw(); // } // -// [Fact] +// [Test] // public void Address_Constructor_From_Raw_Bytes_Should_Throw_On_Bad_Network() // { // var wrongNetworkType = (byte) 255; @@ -58,7 +58,7 @@ // new Action(() => new Address(fullAddress)).Should().Throw(); // } // -// [Fact] +// [Test] // public void Address_Constructor_From_Raw_Bytes_Should_Throw_On_Bad_SmartContract_Byte() // { // var NetworkType = (byte) 1; @@ -68,7 +68,7 @@ // new Action(() => new Address(fullAddress)).Should().Throw(); // } // -// [Fact] +// [Test] // public void Address_Constructor_From_Raw_Bytes_Should_Throw_On_Bad_Byte_Length() // { // var NetworkType = (byte) 1; @@ -81,7 +81,7 @@ // new Action(() => new Address(fullAddress)).Should().Throw(); // } // -// [Fact] +// [Test] // public void Address_Constructor_From_Raw_Bytes_Should_Work_On_Correct_Bytes() // { // var NetworkType = (byte) (int) Network.Devnet; @@ -94,7 +94,7 @@ // address.RawBytes.Should().EndWith(_noPrefixBytes); // } // -// [Fact] +// [Test] // public void Address_Constructor_From_IPublicKey_Should_Fail_On_Null_Public_Key() // { // new Action(() => new Address(null, diff --git a/src/Catalyst.Protocol.Tests/UnitTests/Validators/TransactionBroadcastTests.cs b/src/Catalyst.Protocol.Tests/UnitTests/Validators/TransactionBroadcastTests.cs index cb9645df63..1dfa32d09e 100644 --- a/src/Catalyst.Protocol.Tests/UnitTests/Validators/TransactionBroadcastTests.cs +++ b/src/Catalyst.Protocol.Tests/UnitTests/Validators/TransactionBroadcastTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -33,7 +33,7 @@ //using Google.Protobuf.WellKnownTypes; //using NSubstitute; //using Serilog; -//using Xunit; +//using NUnit.Framework; //namespace Catalyst.Protocol.Tests.Validators //{ @@ -51,7 +51,7 @@ // _transactionValidator = new TransactionValidator(logger, _cryptoWrapper); // } -// [Fact] +// [Test] // public void Can_Allow_Zero_Amount_Transaction_When_Calling_Smart_Contract_Method() // { // GenerateTransaction(TransactionType.Normal); @@ -62,7 +62,7 @@ // AssertTransaction(true); // } -// [Fact] +// [Test] // public void Can_Reject_Contract_Transaction_If_Deployment_Data_And_Call_Data_Exist() // { // GenerateTransaction(TransactionType.Normal); @@ -73,7 +73,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_ST_Entries_Inside_Conf_Transaction() // { // GenerateTransaction(TransactionType.Confidential); @@ -82,7 +82,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_CF_Entries_Inside_Public_Transaction() // { // GenerateTransaction(TransactionType.Normal); @@ -91,7 +91,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_No_Signature() // { // GenerateTransaction(TransactionType.Normal); @@ -99,7 +99,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_Invalid_From_Public_Key() // { // GenerateTransaction(TransactionType.Normal); @@ -108,7 +108,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_Invalid_Signature() // { // GenerateTransaction(TransactionType.Normal); @@ -116,7 +116,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_Any_ST_Entries_In_Smart_Contract_Deployment() // { // GenerateTransaction(TransactionType.Normal); @@ -125,7 +125,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_Any_CF_Entries_In_Smart_Contract_Deployment() // { // GenerateTransaction(TransactionType.Confidential); @@ -134,7 +134,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_Normal_Transactions_With_No_Entries() // { // GenerateTransaction(TransactionType.Normal); @@ -143,7 +143,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_Conf_Transactions_With_No_Entries() // { // GenerateTransaction(TransactionType.Confidential); @@ -152,7 +152,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_Null_Timestamp() // { // GenerateTransaction(TransactionType.Normal); @@ -161,7 +161,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_Invalid_Public_Key_On_ST_Entries() // { // GenerateTransaction(TransactionType.Normal); @@ -170,7 +170,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_Invalid_Amount_On_ST_Entries() // { // GenerateTransaction(TransactionType.Normal); @@ -179,7 +179,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_Invalid_Pedersen_Commit_On_CF_Entries() // { // GenerateTransaction(TransactionType.Confidential); @@ -189,7 +189,7 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Reject_Invalid_Public_Key_On_CF_Entries() // { // GenerateTransaction(TransactionType.Confidential); @@ -199,14 +199,14 @@ // AssertTransaction(false); // } -// [Fact] +// [Test] // public void Can_Pass_Successful_Normal_Transaction() // { // GenerateTransaction(TransactionType.Normal); // AssertTransaction(true); // } -// [Fact] +// [Test] // public void Can_Pass_Successful_Conf_Transaction() // { // GenerateTransaction(TransactionType.Confidential); diff --git a/src/Catalyst.Protocol.Tests/UnitTests/Wire/CandidateDeltaBroadcastTests.cs b/src/Catalyst.Protocol.Tests/UnitTests/Wire/CandidateDeltaBroadcastTests.cs index 970134a6a1..1be9e048bb 100644 --- a/src/Catalyst.Protocol.Tests/UnitTests/Wire/CandidateDeltaBroadcastTests.cs +++ b/src/Catalyst.Protocol.Tests/UnitTests/Wire/CandidateDeltaBroadcastTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,54 +21,55 @@ #endregion +using Catalyst.Core.Lib.Extensions; using Catalyst.Protocol.Wire; using Catalyst.TestUtils; using FluentAssertions; using Google.Protobuf; -using Xunit; +using NUnit.Framework; +using System.Collections.Generic; namespace Catalyst.Protocol.Tests.UnitTests.Wire { public sealed class CandidateDeltaTests { - private sealed class InvalidCandidateDeltaBroadCasts : TheoryData + private sealed class InvalidCandidateDeltaBroadCasts : List { public InvalidCandidateDeltaBroadCasts() { - AddRow(new CandidateDeltaBroadcast + Add(new CandidateDeltaBroadcast { - ProducerId = null, + Producer = ByteString.Empty, Hash = ByteString.CopyFromUtf8("hash"), PreviousDeltaDfsHash = ByteString.CopyFromUtf8("yes") }); - AddRow(new CandidateDeltaBroadcast + Add(new CandidateDeltaBroadcast { - ProducerId = PeerIdHelper.GetPeerId("hello"), + Producer = MultiAddressHelper.GetAddress("hello").GetKvmAddressByteString(), Hash = ByteString.Empty, PreviousDeltaDfsHash = ByteString.CopyFromUtf8("yes") }); - AddRow(new CandidateDeltaBroadcast + Add(new CandidateDeltaBroadcast { - ProducerId = PeerIdHelper.GetPeerId("hello"), + Producer = MultiAddressHelper.GetAddress("hello").GetKvmAddressByteString(), Hash = ByteString.CopyFromUtf8("yes"), PreviousDeltaDfsHash = ByteString.Empty }); } } - [Theory] - [ClassData(typeof(InvalidCandidateDeltaBroadCasts))] + [TestCaseSource(typeof(InvalidCandidateDeltaBroadCasts))] public void CandidateDeltaBroadcast_IsValid_Should_Return_False_On_Invalid_CandidateDeltaBroadcast(CandidateDeltaBroadcast candidate) { candidate.IsValid().Should().BeFalse(); } - [Fact] + [Test] public void CandidateDeltaBroadcast_IsValid_Should_Not_Throw_On_Valid_CandidateDeltaBroadcast() { var candidate = new CandidateDeltaBroadcast { - ProducerId = PeerIdHelper.GetPeerId("hello"), + Producer = MultiAddressHelper.GetAddress("hello").GetKvmAddressByteString(), Hash = ByteString.CopyFromUtf8("yes"), PreviousDeltaDfsHash = ByteString.CopyFromUtf8("bla") }; diff --git a/src/Catalyst.Protocol.Tests/UnitTests/Wire/FavouriteDeltaBroadcastTests.cs b/src/Catalyst.Protocol.Tests/UnitTests/Wire/FavouriteDeltaBroadcastTests.cs index 06d51798f9..8855f58510 100644 --- a/src/Catalyst.Protocol.Tests/UnitTests/Wire/FavouriteDeltaBroadcastTests.cs +++ b/src/Catalyst.Protocol.Tests/UnitTests/Wire/FavouriteDeltaBroadcastTests.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,81 +24,82 @@ using Catalyst.Core.Lib.Extensions; using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; +using Catalyst.TestUtils; using FluentAssertions; using Google.Protobuf; -using Xunit; +using NUnit.Framework; +using System.Collections.Generic; using CandidateDeltaBroadcast = Catalyst.Protocol.Wire.CandidateDeltaBroadcast; namespace Catalyst.Protocol.Tests.UnitTests.Wire { public sealed class FavouriteDeltaBroadcastTests { - private sealed class InvalidFavouriteDeltaBroadcasts : TheoryData + private sealed class InvalidFavouriteDeltaBroadcasts : List { public InvalidFavouriteDeltaBroadcasts() { - AddRow(new FavouriteDeltaBroadcast + Add(new FavouriteDeltaBroadcast { Candidate = new CandidateDeltaBroadcast { - ProducerId = null, + Producer = ByteString.Empty, Hash = ByteString.CopyFromUtf8("hash"), PreviousDeltaDfsHash = ByteString.CopyFromUtf8("yes") }, - VoterId = new PeerId() + Voter = MultiAddressHelper.GetAddress().GetKvmAddressByteString() }); - AddRow(new FavouriteDeltaBroadcast + Add(new FavouriteDeltaBroadcast { Candidate = new CandidateDeltaBroadcast { - ProducerId = new PeerId(), + Producer = MultiAddressHelper.GetAddress().GetKvmAddressByteString(), Hash = ByteString.Empty, PreviousDeltaDfsHash = ByteString.CopyFromUtf8("yes") }, - VoterId = new PeerId() + Voter = MultiAddressHelper.GetAddress().GetKvmAddressByteString() }); - AddRow(new FavouriteDeltaBroadcast + Add(new FavouriteDeltaBroadcast { Candidate = new CandidateDeltaBroadcast { - ProducerId = new PeerId(), + Producer = MultiAddressHelper.GetAddress().GetKvmAddressByteString(), Hash = ByteString.CopyFromUtf8("hash"), PreviousDeltaDfsHash = ByteString.Empty }, - VoterId = new PeerId() + Voter = MultiAddressHelper.GetAddress().GetKvmAddressByteString() }); - AddRow(new FavouriteDeltaBroadcast + Add(new FavouriteDeltaBroadcast { Candidate = new CandidateDeltaBroadcast { - ProducerId = new PeerId(), + Producer = MultiAddressHelper.GetAddress().GetKvmAddressByteString(), Hash = ByteString.CopyFromUtf8("hash"), PreviousDeltaDfsHash = ByteString.CopyFromUtf8("ok") }, - VoterId = null + Voter = ByteString.Empty }); } } - [Theory] - [ClassData(typeof(InvalidFavouriteDeltaBroadcasts))] + [TestCaseSource(typeof(InvalidFavouriteDeltaBroadcasts))] public void FavouriteDeltaBroadcast_IsValid_Should_Throw_On_Invalid_FavouriteDeltaBroadcast(FavouriteDeltaBroadcast favourite) { favourite.IsValid().Should().BeFalse(); } - [Fact] + [Test] public void FavouriteDeltaBroadcast_IsValid_Should_Not_Throw_On_Valid_FavouriteDeltaBroadcast() { var candidate = new FavouriteDeltaBroadcast { Candidate = new CandidateDeltaBroadcast { - ProducerId = new PeerId {PublicKey = "producer".ToUtf8ByteString()}, + Producer = MultiAddressHelper.GetAddress("producer").GetKvmAddressByteString(), Hash = ByteString.CopyFromUtf8("hash"), PreviousDeltaDfsHash = ByteString.CopyFromUtf8("ok") }, - VoterId = new PeerId {PublicKey = "voter".ToUtf8ByteString()}, + Voter = MultiAddressHelper.GetAddress("voter").GetKvmAddressByteString(), }; candidate.IsValid().Should().BeTrue(); } diff --git a/src/Catalyst.Protocol/Account/Address.cs b/src/Catalyst.Protocol/Account/Address.cs index e436ec998a..87f5b178d0 100644 --- a/src/Catalyst.Protocol/Account/Address.cs +++ b/src/Catalyst.Protocol/Account/Address.cs @@ -2,7 +2,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Protocol/Catalyst.Protocol.csproj b/src/Catalyst.Protocol/Catalyst.Protocol.csproj index 5af586d3d6..53049695eb 100644 --- a/src/Catalyst.Protocol/Catalyst.Protocol.csproj +++ b/src/Catalyst.Protocol/Catalyst.Protocol.csproj @@ -1,24 +1,28 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Protocol - James Kirby (nshcore@protonmail.com) + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.Protocol.snk true + + 1701;1702;CS8002 + - - - - + + + + - + + diff --git a/src/Catalyst.Protocol/Cryptography/Signature.cs b/src/Catalyst.Protocol/Cryptography/Signature.cs index 09b2a3a576..e43fae6323 100644 --- a/src/Catalyst.Protocol/Cryptography/Signature.cs +++ b/src/Catalyst.Protocol/Cryptography/Signature.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Protocol/Cryptography/SigningContext.cs b/src/Catalyst.Protocol/Cryptography/SigningContext.cs index dc723772ea..7087c668ef 100644 --- a/src/Catalyst.Protocol/Cryptography/SigningContext.cs +++ b/src/Catalyst.Protocol/Cryptography/SigningContext.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.Protocol/Deltas/Delta.cs b/src/Catalyst.Protocol/Deltas/Delta.cs index deb94d8947..cd28343844 100644 --- a/src/Catalyst.Protocol/Deltas/Delta.cs +++ b/src/Catalyst.Protocol/Deltas/Delta.cs @@ -1,7 +1,7 @@ -#region LICENSE +#region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -51,4 +51,4 @@ public bool IsValid() // update proto public ulong GasLimit { get; set; } = 8_000_000UL; } -} \ No newline at end of file +} diff --git a/src/Catalyst.Protocol/Extensions.cs b/src/Catalyst.Protocol/Extensions.cs new file mode 100644 index 0000000000..f9310f13df --- /dev/null +++ b/src/Catalyst.Protocol/Extensions.cs @@ -0,0 +1,77 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Buffers; +using Google.Protobuf; + +namespace Catalyst.Protocol +{ + /// + /// Provides various extensions for serialization. + /// + public static class Extensions + { + private static readonly ArrayPool Pool = ArrayPool.Shared; + + /// + /// Serializes a message using a pooled array. + /// + /// The message to be serialized. + /// By-ref struct that should be disposed at the end of the usage. + public static PooledSerializedMessage SerializeToPooledBytes(this IMessage message) + { + var messageSize = message.CalculateSize(); + var array = Pool.Rent(messageSize); + + using (CodedOutputStream output = new(array)) + { + message.WriteTo(output); + } + + return new PooledSerializedMessage(array, messageSize); + } + + /// + /// The serialized message wrapped in a scope, a disposable-like by-ref struct. + /// + public ref struct PooledSerializedMessage + { + private readonly byte[] _array; + private readonly int _size; + + public PooledSerializedMessage(byte[] array, int size) + { + _array = array; + _size = size; + } + + public ReadOnlySpan Span => _array.AsSpan(0, _size); + + /// + /// Returns the underlying pool to array. + /// + public void Dispose() => Pool.Return(_array); + } + } +} diff --git a/src/Catalyst.Protocol/Peer/PeerId.cs b/src/Catalyst.Protocol/Peer/PeerId.cs index dc80ef9423..82ecf1494e 100644 --- a/src/Catalyst.Protocol/Peer/PeerId.cs +++ b/src/Catalyst.Protocol/Peer/PeerId.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,37 +29,37 @@ namespace Catalyst.Protocol.Peer { - public partial class PeerId - { - private static readonly ILogger Logger = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType); + //public partial class PeerId + //{ + // private static readonly ILogger Logger = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType); - public IPAddress IpAddress => GenerateIpFromByteString(Ip); - public IPEndPoint IpEndPoint => new IPEndPoint(IpAddress, (int) Port); + // public IPAddress IpAddress => GenerateIpFromByteString(Ip); + // public IPEndPoint IpEndPoint => new IPEndPoint(IpAddress, (int) Port); - //Add support for IPv6 - private IPAddress GenerateIpFromByteString(ByteString ipAddressByteString) - { - var ipv4Buffer = new byte[4]; - var ipAddressBytes = ipAddressByteString.ToByteArray(); - Buffer.BlockCopy(ipAddressBytes, 12, ipv4Buffer, 0, 4); - return new IPAddress(ipAddressBytes).MapToIPv4(); - } + // //Add support for IPv6 + // private IPAddress GenerateIpFromByteString(ByteString ipAddressByteString) + // { + // var ipv4Buffer = new byte[4]; + // var ipAddressBytes = ipAddressByteString.ToByteArray(); + // Buffer.BlockCopy(ipAddressBytes, 12, ipv4Buffer, 0, 4); + // return new IPAddress(ipAddressBytes).MapToIPv4(); + // } - public bool IsValid() - { - if (PublicKey == null || PublicKey.IsEmpty) - { - Logger.Debug("{field} cannot be null or empty", nameof(PublicKey)); - return false; - } + // public bool IsValid() + // { + // if (PublicKey == null || PublicKey.IsEmpty) + // { + // Logger.Debug("{field} cannot be null or empty", nameof(PublicKey)); + // return false; + // } - if (Port > ushort.MaxValue) - { - Logger.Debug("{field} should have a value between 0 and {maxValue}", nameof(Port), ushort.MaxValue); - return false; - } + // if (Port > ushort.MaxValue) + // { + // Logger.Debug("{field} should have a value between 0 and {maxValue}", nameof(Port), ushort.MaxValue); + // return false; + // } - return true; - } - } + // return true; + // } + //} } diff --git a/src/Catalyst.Protocol/Transaction/ConfidentialEntry.cs b/src/Catalyst.Protocol/Transaction/ConfidentialEntry.cs index 4f2beff69a..d81f93c253 100644 --- a/src/Catalyst.Protocol/Transaction/ConfidentialEntry.cs +++ b/src/Catalyst.Protocol/Transaction/ConfidentialEntry.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,14 +32,14 @@ public partial class ConfidentialEntry public bool IsValid() { - if (!RangeProof.IsEmpty && !PedersenCommitment.IsEmpty) return true; + if (RangeProof != null && !PedersenCommitment.IsEmpty) return true; if (PedersenCommitment.IsEmpty) { Logger.Debug("{field} cannot be empty", nameof(PedersenCommitment)); } - if (RangeProof.IsEmpty) + if (RangeProof != null) { Logger.Debug("{field} cannot be empty", nameof(RangeProof)); } diff --git a/src/Catalyst.Protocol/Transaction/ContractEntry.cs b/src/Catalyst.Protocol/Transaction/ContractEntry.cs deleted file mode 100644 index afec1da506..0000000000 --- a/src/Catalyst.Protocol/Transaction/ContractEntry.cs +++ /dev/null @@ -1,63 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using Nethermind.Dirichlet.Numerics; - -namespace Catalyst.Protocol.Transaction -{ - public partial class ContractEntry - { - public bool IsValid() - { - // data can be empty - return GasLimit >= 21000; // make it a constant - MIN_GAS_LIMIT - } - - // add to proto - /// - /// Gas limit for the entry expressed in gas units. - /// - public ulong GasLimit { get; set; } - - // add to proto - /// - /// Gas price to use as a multiplier of gas cost expressed in units - /// to arrive at the total gas cost expressed in ETH. - /// - public UInt256 GasPrice { get; set; } - - /// - /// If this is an entry that is about to deploy a smart contract then true, - /// otherwise false. - /// - public bool IsValidDeploymentEntry => IsValid() && Base.ReceiverPublicKey.IsEmpty; - - /// - /// If this is an entry that is about to call a smart contract then true, - /// otherwise false. - /// - public bool IsValidCallEntry => IsValid() && !Base.ReceiverPublicKey.IsEmpty; - - public byte[] TargetContract { get; set; } - } -} diff --git a/src/Catalyst.Protocol/Transaction/PublicEntry.cs b/src/Catalyst.Protocol/Transaction/PublicEntry.cs index 72b2e0ffba..5bcc4beaa6 100644 --- a/src/Catalyst.Protocol/Transaction/PublicEntry.cs +++ b/src/Catalyst.Protocol/Transaction/PublicEntry.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,13 @@ #endregion -using System.Linq; -using System.Reflection; +using Google.Protobuf.WellKnownTypes; using Serilog; +using System.Reflection; +using Nethermind.Dirichlet.Numerics; +using System; +using Google.Protobuf; +using MultiFormats; namespace Catalyst.Protocol.Transaction { @@ -31,15 +35,56 @@ public partial class PublicEntry { private static readonly ILogger Logger = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType); + //Props like Amount and Gas price can contain null bytes appended to the end that can cause a different + //Id when converted back and forth. I assume this is comming from the web provider. + private byte[] TrimEnd(byte[] array) + { + int lastIndex = Array.FindLastIndex(array, b => b != 0); + + Array.Resize(ref array, lastIndex + 1); + + return array; + } + + //Give the same TransactionId everytime. + public MultiHash GetId(string algorithmName) + { + var publicEntryClone = new PublicEntry(this); + publicEntryClone.Amount = ByteString.CopyFrom(TrimEnd(publicEntryClone.Amount.ToByteArray())); + publicEntryClone.GasPrice = ByteString.CopyFrom(TrimEnd(publicEntryClone.GasPrice.ToByteArray())); + return MultiHash.ComputeHash(publicEntryClone.ToByteArray(), algorithmName); + } + public bool IsValid() { - if (Amount == null || Amount.IsEmpty || Amount.All(b => b == default)) + // make it a constant - MIN_GAS_LIMIT + if (GasLimit < 21000) { - Logger.Debug("{field} cannot be 0", nameof(Amount)); return false; } return true; + + // TODO: reconsider signature + + //var hasValidSignature = Signature.IsValid(SignatureType.TransactionPublic); + //return hasValidSignature; } + + /// bytes + /// If this is an entry that is about to deploy a smart contract then true, + /// otherwise false. + /// + public bool IsValidDeploymentEntry => IsValid() && ReceiverAddress.IsEmpty; + + /// + /// If this is an entry that is about to call a smart contract then true, + /// otherwise false. + /// + public bool IsValidCallEntry => IsValid() && !ReceiverAddress.IsEmpty; + + public bool IsContractDeployment => IsValidDeploymentEntry; + public bool IsContractCall => IsValidCallEntry; + public bool IsPublicTransaction => IsValid(); } } diff --git a/src/Catalyst.Protocol/Wire/CandidateDeltaBroadcast.cs b/src/Catalyst.Protocol/Wire/CandidateDeltaBroadcast.cs index f957e25965..0004b93b91 100644 --- a/src/Catalyst.Protocol/Wire/CandidateDeltaBroadcast.cs +++ b/src/Catalyst.Protocol/Wire/CandidateDeltaBroadcast.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,6 +22,8 @@ #endregion using System.Reflection; +using Google.Protobuf; +using MultiFormats; using Serilog; namespace Catalyst.Protocol.Wire @@ -32,9 +34,9 @@ public sealed partial class CandidateDeltaBroadcast public bool IsValid() { - if (ProducerId == null) + if (Producer == null || Producer == ByteString.Empty) { - Logger.Debug("{field} cannot be null", nameof(ProducerId)); + Logger.Debug("{field} is a invalid address", nameof(Producer)); return false; } @@ -44,13 +46,13 @@ public bool IsValid() return false; } - if (Hash == null || Hash.IsEmpty) + if (Hash != null && !Hash.IsEmpty) { - Logger.Debug("{field} cannot be null or empty", nameof(Hash)); - return false; + return true; } - - return true; + + Logger.Debug("{field} cannot be null or empty", nameof(Hash)); + return false; } } } diff --git a/src/Catalyst.Protocol/Wire/FavouriteDeltaBroadcast.cs b/src/Catalyst.Protocol/Wire/FavouriteDeltaBroadcast.cs index 301791487c..76598fe1f2 100644 --- a/src/Catalyst.Protocol/Wire/FavouriteDeltaBroadcast.cs +++ b/src/Catalyst.Protocol/Wire/FavouriteDeltaBroadcast.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,6 +22,8 @@ #endregion using System.Reflection; +using Google.Protobuf; +using MultiFormats; using Serilog; namespace Catalyst.Protocol.Wire @@ -38,13 +40,13 @@ public bool IsValid() return false; } - if (VoterId == null) + if (Voter == null || Voter == ByteString.Empty) { - Logger.Debug("{field} cannot be null", nameof(VoterId)); + Logger.Debug("{field} is a invalid address", nameof(Voter)); return false; } - return Candidate.IsValid() && VoterId.IsValid(); + return Candidate.IsValid(); } } } diff --git a/src/Catalyst.Protocol/Wire/TransactionBroadcast.cs b/src/Catalyst.Protocol/Wire/TransactionBroadcast.cs index e387a10875..029a94b577 100644 --- a/src/Catalyst.Protocol/Wire/TransactionBroadcast.cs +++ b/src/Catalyst.Protocol/Wire/TransactionBroadcast.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,113 +21,7 @@ #endregion -using System.Linq; -using System.Numerics; -using System.Reflection; -using Catalyst.Protocol.Cryptography; -using Google.Protobuf.WellKnownTypes; -using Nethermind.Dirichlet.Numerics; -using Serilog; - namespace Catalyst.Protocol.Wire { - public partial class TransactionBroadcast - { - private static readonly ILogger Logger = Log.Logger.ForContext(MethodBase.GetCurrentMethod().DeclaringType); - - partial void OnConstruction() - { - IsContractDeployment = ContractEntries.Any(c => c.IsValidDeploymentEntry); - IsContractCall = ContractEntries.Any(c => c.IsValidCallEntry); - IsPublicTransaction = PublicEntries.Any() && PublicEntries.All(e => e.IsValid()); - IsConfidentialTransaction = ConfidentialEntries.Any() - && ConfidentialEntries.All(e => e.IsValid()); - } - - /// - /// OnConstruction does not generate valid transactions for tests / in general - need to discuss - /// - public void AfterConstruction() - { - IsContractDeployment = ContractEntries.Any(c => c.IsValidDeploymentEntry); - IsContractCall = ContractEntries.Any(c => c.IsValidCallEntry); - IsPublicTransaction = PublicEntries.Any() && PublicEntries.All(e => e.IsValid()); - IsConfidentialTransaction = ConfidentialEntries.Any() - && ConfidentialEntries.All(e => e.IsValid()); - } - - // why TransactionBroadcastTest is commented out? if brought back - this needs to have a test coverage there too - public UInt256 AverageGasPrice - { - get - { - var averagePrice = BigInteger.Zero; - ulong totalLimit = 0; - var count = ContractEntries.Count; - for (var i = 0; i < count; i++) - { - var limit = ContractEntries[i].GasLimit; - totalLimit += limit; - averagePrice += limit * ContractEntries[i].GasPrice; - } - - if (totalLimit == 0) - { - return UInt256.Zero; - } - - UInt256.Create(out var result, averagePrice / totalLimit); - return result; - } - } - - // why TransactionBroadcastTest is commented out? if brought back - this needs to have a test coverage there too - public ulong TotalGasLimit - { - get - { - ulong totalLimit = 0; - var count = ContractEntries.Count; - for (var i = 0; i < count; i++) - { - totalLimit += ContractEntries[i].GasLimit; - } - - return totalLimit; - } - } - - public bool IsContractDeployment { get; private set; } - public bool IsContractCall { get; private set; } - public bool IsPublicTransaction { get; private set; } - public bool IsConfidentialTransaction { get; private set; } - - public bool HasValidEntries() - { - var hasSingleType = IsContractDeployment ^ IsContractCall ^ IsPublicTransaction ^ IsConfidentialTransaction; - if (hasSingleType) - { - return true; - } - - Logger.Debug("{instance} can only be of a single type", nameof(TransactionBroadcast)); - return false; - } - - public bool IsValid() - { - var isTimestampValid = Timestamp != default(Timestamp) && Timestamp != new Timestamp(); - if (!isTimestampValid) - { - Logger.Debug("{timestamp} cannot be null or 0."); - return false; - } - - var hasValidSignature = Signature.IsValid(IsConfidentialTransaction - ? SignatureType.TransactionConfidential - : SignatureType.TransactionPublic); - - return hasValidSignature && HasValidEntries(); - } - } + public partial class TransactionBroadcast { } } diff --git a/src/Catalyst.Protocol/project.assets.json b/src/Catalyst.Protocol/project.assets.json deleted file mode 100644 index af3648a609..0000000000 --- a/src/Catalyst.Protocol/project.assets.json +++ /dev/null @@ -1,204 +0,0 @@ -{ - "version": 3, - "targets": { - ".NETCoreApp,Version=v2.2": { - "Dawn.Guard/1.9.0": { - "type": "package", - "compile": { - "lib/netstandard2.0/Dawn.Guard.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Dawn.Guard.dll": {} - } - }, - "Google.Protobuf/3.9.1": { - "type": "package", - "dependencies": { - "System.Memory": "4.5.2" - }, - "compile": { - "lib/netstandard2.0/Google.Protobuf.dll": {} - }, - "runtime": { - "lib/netstandard2.0/Google.Protobuf.dll": {} - } - }, - "Grpc.Tools/1.22.0": { - "type": "package", - "build": { - "build/Grpc.Tools.props": {}, - "build/Grpc.Tools.targets": {} - } - }, - "System.Memory/4.5.2": { - "type": "package", - "compile": { - "ref/netcoreapp2.1/_._": {} - }, - "runtime": { - "lib/netcoreapp2.1/_._": {} - } - } - } - }, - "libraries": { - "Dawn.Guard/1.9.0": { - "sha512": "xqenw7h4BHYMlCK9IeMNHa+db+kfjvnfGrqABHnNT053EvE6rYQS545Q+wTEU5sBZiEW7CvcThWm/pdWalu3cg==", - "type": "package", - "path": "dawn.guard/1.9.0", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "dawn.guard.1.9.0.nupkg.sha512", - "dawn.guard.nuspec", - "lib/netstandard1.0/Dawn.Guard.dll", - "lib/netstandard1.0/Dawn.Guard.xml", - "lib/netstandard2.0/Dawn.Guard.dll", - "lib/netstandard2.0/Dawn.Guard.xml" - ] - }, - "Google.Protobuf/3.9.1": { - "sha512": "qCs2Tn+hJSllwIjQ2HQ4uNYGLbXWr3D6jJPFlJkIu1pErk4eQB1qlUcjA7NxA4JwOJzQbkZL45EbPGT/ZgwtsQ==", - "type": "package", - "path": "google.protobuf/3.9.1", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "google.protobuf.3.9.1.nupkg.sha512", - "google.protobuf.nuspec", - "lib/net45/Google.Protobuf.dll", - "lib/net45/Google.Protobuf.pdb", - "lib/net45/Google.Protobuf.xml", - "lib/netstandard1.0/Google.Protobuf.dll", - "lib/netstandard1.0/Google.Protobuf.pdb", - "lib/netstandard1.0/Google.Protobuf.xml", - "lib/netstandard2.0/Google.Protobuf.dll", - "lib/netstandard2.0/Google.Protobuf.pdb", - "lib/netstandard2.0/Google.Protobuf.xml" - ] - }, - "Grpc.Tools/1.22.0": { - "sha512": "4ZP7tKR2//jeFm/cU1WNBZxEhbkppNYOxHsExCA0D/ugbt7YrLUZD0IfouyT7KspMshQwaGBNf/ns+dSZyl08w==", - "type": "package", - "path": "grpc.tools/1.22.0", - "hasTools": true, - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "build/Grpc.Tools.props", - "build/Grpc.Tools.targets", - "build/_grpc/Grpc.CSharp.xml", - "build/_grpc/_Grpc.Tools.props", - "build/_grpc/_Grpc.Tools.targets", - "build/_protobuf/Google.Protobuf.Tools.props", - "build/_protobuf/Google.Protobuf.Tools.targets", - "build/_protobuf/Protobuf.CSharp.xml", - "build/_protobuf/net45/Protobuf.MSBuild.dll", - "build/_protobuf/netstandard1.3/Protobuf.MSBuild.dll", - "build/native/Grpc.Tools.props", - "build/native/include/google/protobuf/any.proto", - "build/native/include/google/protobuf/api.proto", - "build/native/include/google/protobuf/descriptor.proto", - "build/native/include/google/protobuf/duration.proto", - "build/native/include/google/protobuf/empty.proto", - "build/native/include/google/protobuf/field_mask.proto", - "build/native/include/google/protobuf/source_context.proto", - "build/native/include/google/protobuf/struct.proto", - "build/native/include/google/protobuf/timestamp.proto", - "build/native/include/google/protobuf/type.proto", - "build/native/include/google/protobuf/wrappers.proto", - "grpc.tools.1.22.0.nupkg.sha512", - "grpc.tools.nuspec", - "tools/linux_x64/grpc_csharp_plugin", - "tools/linux_x64/protoc", - "tools/linux_x86/grpc_csharp_plugin", - "tools/linux_x86/protoc", - "tools/macosx_x64/grpc_csharp_plugin", - "tools/macosx_x64/protoc", - "tools/macosx_x86/grpc_csharp_plugin", - "tools/macosx_x86/protoc", - "tools/windows_x64/grpc_csharp_plugin.exe", - "tools/windows_x64/protoc.exe", - "tools/windows_x86/grpc_csharp_plugin.exe", - "tools/windows_x86/protoc.exe" - ] - }, - "System.Memory/4.5.2": { - "sha512": "fvq1GNmUFwbKv+aLVYYdgu/+gc8Nu9oFujOxIjPrsf+meis9JBzTPDL6aP/eeGOz9yPj6rRLUbOjKMpsMEWpNg==", - "type": "package", - "path": "system.memory/4.5.2", - "files": [ - ".nupkg.metadata", - ".signature.p7s", - "LICENSE.TXT", - "THIRD-PARTY-NOTICES.TXT", - "lib/netcoreapp2.1/_._", - "lib/netstandard1.1/System.Memory.dll", - "lib/netstandard1.1/System.Memory.xml", - "lib/netstandard2.0/System.Memory.dll", - "lib/netstandard2.0/System.Memory.xml", - "ref/netcoreapp2.1/_._", - "system.memory.4.5.2.nupkg.sha512", - "system.memory.nuspec", - "useSharedDesignerContext.txt", - "version.txt" - ] - } - }, - "projectFileDependencyGroups": { - ".NETCoreApp,Version=v2.2": [ - "Dawn.Guard >= 1.9.0", - "Google.Protobuf >= 3.9.1", - "Grpc.Tools >= 1.22.0" - ] - }, - "packageFolders": { - "/home/nsh/.nuget/packages/": {} - }, - "project": { - "version": "1.0.0", - "restore": { - "projectUniqueName": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Protocol/Catalyst.Protocol.csproj", - "projectName": "Catalyst.Protocol", - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Protocol/Catalyst.Protocol.csproj", - "packagesPath": "/home/nsh/.nuget/packages/", - "outputPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Protocol/", - "projectStyle": "PackageReference", - "configFilePaths": [ - "/home/nsh/RiderProjects/Catalyst.Node/src/nuget.config", - "/home/nsh/.config/NuGet/NuGet.Config" - ], - "originalTargetFrameworks": [ - "netcoreapp2.2" - ], - "sources": { - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "netcoreapp2.2": { - "projectReferences": {} - } - } - }, - "frameworks": { - "netcoreapp2.2": { - "dependencies": { - "Dawn.Guard": { - "target": "Package", - "version": "[1.9.0, )" - }, - "Google.Protobuf": { - "target": "Package", - "version": "[3.9.1, )" - }, - "Grpc.Tools": { - "include": "Runtime, Build, Native, ContentFiles, Analyzers", - "suppressParent": "All", - "target": "Package", - "version": "[1.22.0, )" - } - } - } - } - } -} \ No newline at end of file diff --git a/src/Catalyst.Protocol/project.packagespec.json b/src/Catalyst.Protocol/project.packagespec.json deleted file mode 100644 index e11f4299c5..0000000000 --- a/src/Catalyst.Protocol/project.packagespec.json +++ /dev/null @@ -1,41 +0,0 @@ -{ - "version": "1.0.0", - "restore": { - "projectUniqueName": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Protocol/Catalyst.Protocol.csproj", - "projectName": "Catalyst.Protocol", - "projectPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Protocol/Catalyst.Protocol.csproj", - "outputPath": "/home/nsh/RiderProjects/Catalyst.Node/src/Catalyst.Protocol/", - "projectStyle": "PackageReference", - "originalTargetFrameworks": [ - "netcoreapp2.2" - ], - "sources": { - "https://api.nuget.org/v3/index.json": {} - }, - "frameworks": { - "netcoreapp2.2": { - "projectReferences": {} - } - } - }, - "frameworks": { - "netcoreapp2.2": { - "dependencies": { - "Dawn.Guard": { - "target": "Package", - "version": "[1.9.0, )" - }, - "Google.Protobuf": { - "target": "Package", - "version": "[3.9.1, )" - }, - "Grpc.Tools": { - "include": "Runtime, Build, Native, ContentFiles, Analyzers", - "suppressParent": "All", - "target": "Package", - "version": "[1.22.0, )" - } - } - } - } -} \ No newline at end of file diff --git a/src/Catalyst.Simulator/Catalyst.Simulator.csproj b/src/Catalyst.Simulator/Catalyst.Simulator.csproj index 79ff4a13e1..30dfa03b19 100644 --- a/src/Catalyst.Simulator/Catalyst.Simulator.csproj +++ b/src/Catalyst.Simulator/Catalyst.Simulator.csproj @@ -1,6 +1,6 @@ - + - netcoreapp3.0 + net6.0 Catalyst.Simulator James Kirby (nshcore@protonmail.com) true @@ -8,17 +8,19 @@ true Exe + + 1701;1702;CS8002 + - - + + - diff --git a/src/Catalyst.Simulator/ClientRpcInfo.cs b/src/Catalyst.Simulator/ClientRpcInfo.cs index 9f06a2c927..e94ea97d21 100644 --- a/src/Catalyst.Simulator/ClientRpcInfo.cs +++ b/src/Catalyst.Simulator/ClientRpcInfo.cs @@ -21,20 +21,20 @@ #endregion -using Catalyst.Protocol.Peer; using Catalyst.Simulator.Interfaces; +using MultiFormats; namespace Catalyst.Simulator { public class ClientRpcInfo { - public ClientRpcInfo(PeerId peerIdentifier, IRpcClient rpcClient) + public ClientRpcInfo(MultiAddress address, IRpcClient rpcClient) { - PeerId = peerIdentifier; + Address = address; RpcClient = rpcClient; } - public PeerId PeerId { get; } + public MultiAddress Address { get; } public IRpcClient RpcClient { get; } } } diff --git a/src/Catalyst.Simulator/Helpers/TransactionHelper.cs b/src/Catalyst.Simulator/Helpers/TransactionHelper.cs index 7eb3760dfc..f979254eef 100644 --- a/src/Catalyst.Simulator/Helpers/TransactionHelper.cs +++ b/src/Catalyst.Simulator/Helpers/TransactionHelper.cs @@ -27,9 +27,9 @@ using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.Protocol.Cryptography; using Catalyst.Protocol.Network; -using Catalyst.Protocol.Wire; using Catalyst.Protocol.Rpc.Node; using Catalyst.Protocol.Transaction; +using Catalyst.Protocol.Wire; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; using Nethermind.Dirichlet.Numerics; @@ -44,35 +44,26 @@ public static class TransactionHelper SignatureType = SignatureType.TransactionPublic }; - public static BroadcastRawTransactionRequest GenerateTransaction(uint amount, int fee, int nonce = 0) + public static BroadcastRawTransactionRequest GenerateTransaction(uint amount, int nonce = 0) { var cryptoWrapper = new FfiWrapper(); var privateKey = cryptoWrapper.GeneratePrivateKey(); var publicKey = ByteString.CopyFrom(privateKey.GetPublicKey().Bytes); - + var transaction = new TransactionBroadcast { - PublicEntries = + PublicEntry = new PublicEntry { - new PublicEntry - { - Amount = ((UInt256) amount).ToUint256ByteString(), - Base = new BaseEntry - { - Nonce = (ulong) nonce, - SenderPublicKey = privateKey.GetPublicKey().Bytes.ToByteString(), - ReceiverPublicKey = publicKey, - TransactionFees = ((UInt256) fee).ToUint256ByteString() - } - } - }, - Timestamp = Timestamp.FromDateTime(DateTime.UtcNow), + Amount = ((UInt256) amount).ToUint256ByteString(), + Nonce = (ulong) nonce, + SenderAddress = privateKey.GetPublicKey().Bytes.ToByteString(), + ReceiverAddress = publicKey + }.Sign(cryptoWrapper, privateKey, DevNetPublicTransactionContext) }; - var signedTransaction = transaction.Sign(cryptoWrapper, privateKey, DevNetPublicTransactionContext); var broadcastRawTransactionRequest = new BroadcastRawTransactionRequest { - Transaction = signedTransaction + Transaction = transaction }; return broadcastRawTransactionRequest; diff --git a/src/Catalyst.Simulator/Interfaces/IRpcClient.cs b/src/Catalyst.Simulator/Interfaces/IRpcClient.cs index b12e5bcfdf..edc9f2376d 100644 --- a/src/Catalyst.Simulator/Interfaces/IRpcClient.cs +++ b/src/Catalyst.Simulator/Interfaces/IRpcClient.cs @@ -25,13 +25,14 @@ using System.Threading.Tasks; using Catalyst.Protocol.Peer; using Google.Protobuf; +using MultiFormats; namespace Catalyst.Simulator.Interfaces { public interface IRpcClient { - Task ConnectRetryAsync(PeerId peerIdentifier, int retryAttempts = 5); - Task ConnectAsync(PeerId peerIdentifier); + Task ConnectRetryAsync(MultiAddress Addressentifier, int retryAttempts = 5); + Task ConnectAsync(MultiAddress Addressentifier); void SendMessage(T message) where T : IMessage; void ReceiveMessage(Action message) where T : IMessage; bool IsConnected(); diff --git a/src/Catalyst.Simulator/RpcClients/SimpleRPCClient.cs b/src/Catalyst.Simulator/RpcClients/SimpleRPCClient.cs index 50a9c2ed2c..6f4dde7f76 100644 --- a/src/Catalyst.Simulator/RpcClients/SimpleRPCClient.cs +++ b/src/Catalyst.Simulator/RpcClients/SimpleRPCClient.cs @@ -29,43 +29,40 @@ using Catalyst.Abstractions.Cli; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.Keystore; using Catalyst.Abstractions.P2P; using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Cli; using Catalyst.Core.Lib.Cryptography; using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.FileSystem; -using Catalyst.Core.Lib.IO.EventLoop; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Core.Lib.P2P; using Catalyst.Core.Lib.Rpc.IO.Messaging.Correlation; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Cryptography.BulletProofs; -using Catalyst.Core.Modules.Hashing; using Catalyst.Core.Modules.KeySigner; -using Catalyst.Core.Modules.Keystore; using Catalyst.Core.Modules.Rpc.Client; using Catalyst.Core.Modules.Rpc.Client.IO.Observers; using Catalyst.Core.Modules.Rpc.Client.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; using Catalyst.Protocol.Cryptography; -using Catalyst.Protocol.Peer; using Catalyst.Simulator.Interfaces; -using DotNetty.Transport.Channels; using Google.Protobuf; using Microsoft.Extensions.Caching.Memory; +using MultiFormats; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; namespace Catalyst.Simulator.RpcClients { public class SimpleRpcClient : IRpcClient { private readonly ILogger _logger; - private readonly PeerId _senderPeerId; - private PeerId _recipientPeerId; - private Abstractions.Rpc.IRpcClient _rpcClient; + private readonly MultiAddress _sender; + private MultiAddress _recipientPeerId; + private IRpcClient _rpcClient; private readonly X509Certificate2 _certificate; private readonly RpcClientFactory _rpcClientFactory; @@ -78,22 +75,18 @@ public SimpleRpcClient(IUserOutput userOutput, _logger = logger; _certificate = certificate; - var fileSystem = new FileSystem(); - var consolePasswordReader = new ConsolePasswordReader(userOutput, new ConsoleUserInput()); var passwordManager = new PasswordManager(consolePasswordReader, passwordRegistry); var cryptoContext = new FfiWrapper(); - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); - var peerSettings = Substitute.For(); peerSettings.NetworkType.Returns(signingContextProvider.NetworkType); - var localKeyStore = new LocalKeyStore(passwordManager, cryptoContext, fileSystem, hashProvider, _logger); - + //var localKeyStore = new LocalKeyStore(passwordManager, cryptoContext, Substitute.For(), _logger); + //localKeyStore var keyRegistry = new KeyRegistry(); - var keySigner = new KeySigner(localKeyStore, cryptoContext, keyRegistry); + var keySigner = new KeySigner(cryptoContext, Substitute.For(), keyRegistry); var memoryCacheOptions = new MemoryCacheOptions(); var memoryCache = new MemoryCache(memoryCacheOptions); @@ -122,15 +115,17 @@ public SimpleRpcClient(IUserOutput userOutput, //PeerId for RPC/TCP is currently redundant. var publicKey = keyRegistry.GetItemFromRegistry(KeyRegistryTypes.DefaultKey).GetPublicKey().Bytes; - _senderPeerId = publicKey.BuildPeerIdFromPublicKey(IPAddress.Any, 1026); + + //todo + //_sender = publicKey.BuildPeerIdFromPublicKey(IPAddress.Any, 1026); } - public async Task ConnectRetryAsync(PeerId peerIdentifier, int retryAttempts = 5) + public async Task ConnectRetryAsync(MultiAddress address, int retryAttempts = 5) { var retryCountDown = retryAttempts; while (retryCountDown > 0) { - var isConnectionSuccessful = await ConnectAsync(peerIdentifier).ConfigureAwait(false); + var isConnectionSuccessful = await ConnectAsync(address).ConfigureAwait(false); if (isConnectionSuccessful) { return true; @@ -148,53 +143,58 @@ public async Task ConnectRetryAsync(PeerId peerIdentifier, int retryAttemp return false; } - public async Task ConnectAsync(PeerId peerIdentifier) + public async Task ConnectAsync(MultiAddress address) { - _recipientPeerId = peerIdentifier; - - var peerRpcConfig = new RpcClientSettings - { - HostAddress = _recipientPeerId.IpAddress, - Port = (int) _recipientPeerId.Port, - PublicKey = _recipientPeerId.PublicKey.KeyToString() - }; - - _logger.Information($"Connecting to {peerRpcConfig.HostAddress}:{peerRpcConfig.Port}"); - - try - { - _rpcClient = - await _rpcClientFactory.GetClient(_certificate, peerRpcConfig).ConfigureAwait(false); - return _rpcClient.Channel.Open; - } - catch (ConnectException connectionException) - { - _logger.Error(connectionException, "Could not connect to node"); - } - catch (Exception exception) - { - _logger.Error(exception, "Error attempting to connect to node"); - } + _recipientPeerId = address; + + //todo + //var peerRpcConfig = new RpcClientSettings + //{ + // HostAddress = _recipientPeerId.IpAddress, + // Port = (int) _recipientPeerId.Port, + // PublicKey = _recipientPeerId.PublicKey.KeyToString() + //}; + + //_logger.Information($"Connecting to {peerRpcConfig.HostAddress}:{peerRpcConfig.Port}"); + + //try + //{ + // _rpcClient = + // await _rpcClientFactory.GetClientAsync(_certificate, peerRpcConfig).ConfigureAwait(false); + // return _rpcClient.Channel.Open; + //} + //catch (ConnectException connectionException) + //{ + // _logger.Error(connectionException, "Could not connect to node"); + //} + //catch (Exception exception) + //{ + // _logger.Error(exception, "Error attempting to connect to node"); + //} return false; } - public bool IsConnected() { return _rpcClient.Channel.Active; } + public bool IsConnected() + { + return true; + //_rpcClient.Channel.Active; + } public void SendMessage(T message) where T : IMessage { var protocolMessage = - message.ToProtocolMessage(_senderPeerId, CorrelationId.GenerateCorrelationId()); + message.ToProtocolMessage(_sender, CorrelationId.GenerateCorrelationId()); var messageDto = new MessageDto( protocolMessage, _recipientPeerId); - _rpcClient.SendMessage(messageDto); + //_rpcClient.SendMessage(messageDto); } public void ReceiveMessage(Action message) where T : IMessage { - _rpcClient.SubscribeToResponse(message.Invoke); + //_rpcClient.SubscribeToResponse(message.Invoke); } } } diff --git a/src/Catalyst.Simulator/SimulationNode.cs b/src/Catalyst.Simulator/SimulationNode.cs index 79012813ae..b28bf68689 100644 --- a/src/Catalyst.Simulator/SimulationNode.cs +++ b/src/Catalyst.Simulator/SimulationNode.cs @@ -24,6 +24,7 @@ using System.Net; using Catalyst.Core.Lib.Extensions; using Catalyst.Protocol.Peer; +using MultiFormats; namespace Catalyst.Simulator { @@ -35,9 +36,10 @@ public sealed class SimulationNode public string PublicKey { get; set; } - public PeerId ToPeerIdentifier() + public MultiAddress ToPeerIdentifier() { - return PublicKey.BuildPeerIdFromBase32Key(IPAddress.Parse(Ip), Port); + return null; + //return PublicKey.BuildPeerIdFromBase58Key(IPAddress.Parse(Ip), Port); } } } diff --git a/src/Catalyst.Simulator/Simulations/TransactionSimulation.cs b/src/Catalyst.Simulator/Simulations/TransactionSimulation.cs index 0c77e6fe37..3817187e0d 100644 --- a/src/Catalyst.Simulator/Simulations/TransactionSimulation.cs +++ b/src/Catalyst.Simulator/Simulations/TransactionSimulation.cs @@ -46,10 +46,10 @@ public TransactionSimulation(IUserOutput userOutput) private async Task ConnectAsync(ClientRpcInfo clientRpcInfo) { var isConnectionSuccessful = await clientRpcInfo.RpcClient - .ConnectRetryAsync(clientRpcInfo.PeerId).ConfigureAwait(false); + .ConnectRetryAsync(clientRpcInfo.Address).ConfigureAwait(false); if (!isConnectionSuccessful) { - _userOutput.WriteLine($"Could not connect to node: {clientRpcInfo.PeerId.Ip}:{clientRpcInfo.PeerId.Port}"); + //_userOutput.WriteLine($"Could not connect to node: {clientRpcInfo.Address.Ip}:{clientRpcInfo.Address.Port}"); return false; } @@ -85,7 +85,7 @@ public async Task SimulateAsync(IList clientRpcInfoList) foreach (var clientRpcInfo in clientRpcInfoList) { _userOutput.WriteLine("Sending transaction"); - var transaction = TransactionHelper.GenerateTransaction((uint) _random.Next(1, 100), _random.Next(2)); + var transaction = TransactionHelper.GenerateTransaction((uint) _random.Next(1, 100)); clientRpcInfo.RpcClient.SendMessage(transaction); } diff --git a/src/Catalyst.TestUtils/AccountHelper.cs b/src/Catalyst.TestUtils/AccountHelper.cs index b6ee4feb31..c4a3675d27 100644 --- a/src/Catalyst.TestUtils/AccountHelper.cs +++ b/src/Catalyst.TestUtils/AccountHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -33,7 +33,7 @@ public static Account GetAccount(UInt256 balance, AccountTypes accountType = default, string publicAddress = "0x32Be343B94f860124dC4fEe278FDCBD38C102D88") { - var account = new Account(publicAddress, accountType ?? AccountTypes.Public, balance); + Account account = new(publicAddress, accountType ?? AccountTypes.Public, balance); return account; } } diff --git a/src/Catalyst.TestUtils/CacheHelper.cs b/src/Catalyst.TestUtils/CacheHelper.cs index 1bb7afc024..6add0c4d56 100644 --- a/src/Catalyst.TestUtils/CacheHelper.cs +++ b/src/Catalyst.TestUtils/CacheHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -35,9 +35,9 @@ public static Dictionary MockCacheEvictionCallback(obje { var correlationId = (ByteString) key; var cacheEntry = Substitute.For(); - var expirationTokens = new List(); + List expirationTokens = new(); cacheEntry.ExpirationTokens.Returns(expirationTokens); - var expirationCallbacks = new List(); + List expirationCallbacks = new(); cacheEntry.PostEvictionCallbacks.Returns(expirationCallbacks); cache.CreateEntry(correlationId).Returns(cacheEntry); diff --git a/src/Catalyst.TestUtils/Catalyst.TestUtils.csproj b/src/Catalyst.TestUtils/Catalyst.TestUtils.csproj index 681cd47d74..7acc93f670 100644 --- a/src/Catalyst.TestUtils/Catalyst.TestUtils.csproj +++ b/src/Catalyst.TestUtils/Catalyst.TestUtils.csproj @@ -1,7 +1,7 @@ - + - netcoreapp3.0 - James Kirby (nshcore@protonmail.com) + net6.0 + Darren Priestnall (darren.op@catalystnet.org) true Catalyst.TestUtils.snk true @@ -9,7 +9,9 @@ Catalyst.TestUtils - + + 1701;1702;VSTHRD200;CS8002 + Always @@ -22,18 +24,34 @@ - - - - - - + + + + + + + + + + + + - - + + - + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + diff --git a/src/Catalyst.TestUtils/ComponentWithRepository.cs b/src/Catalyst.TestUtils/ComponentWithRepository.cs index 9c4c5f5225..2974246d0c 100644 --- a/src/Catalyst.TestUtils/ComponentWithRepository.cs +++ b/src/Catalyst.TestUtils/ComponentWithRepository.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.TestUtils/Config/devnet.json b/src/Catalyst.TestUtils/Config/devnet.json index 901df6a4da..e220ecda01 100644 --- a/src/Catalyst.TestUtils/Config/devnet.json +++ b/src/Catalyst.TestUtils/Config/devnet.json @@ -1,7 +1,6 @@ { "CatalystNodeConfiguration": { "Peer": { - "Network": "Devnet", "PublicKey": "hv6vvbt2u567syz5labuqnfabsc3zobfwekl4cy3c574n6vkj7sq", "Port": 42076, "BindAddress": "127.0.0.1", diff --git a/src/Catalyst.TestUtils/Config/mainnet.json b/src/Catalyst.TestUtils/Config/mainnet.json index 2c918efff5..48833bed79 100644 --- a/src/Catalyst.TestUtils/Config/mainnet.json +++ b/src/Catalyst.TestUtils/Config/mainnet.json @@ -1,7 +1,6 @@ { "CatalystNodeConfiguration": { "Peer": { - "Network": "Mainnet", "PublicKey": "hv6vvbt2u567syz5labuqnfabsc3zobfwekl4cy3c574n6vkj7sq", "Port": 42076, "BindAddress": "127.0.0.1", diff --git a/src/Catalyst.TestUtils/Config/nodes.json b/src/Catalyst.TestUtils/Config/nodes.json index 65b59e238e..a345ea71c3 100644 --- a/src/Catalyst.TestUtils/Config/nodes.json +++ b/src/Catalyst.TestUtils/Config/nodes.json @@ -3,19 +3,15 @@ "nodes": [ { "nodeId": "node1", - "host": "127.0.0.1", - "port": 42066, + "Address": "/ip4/127.0.0.1/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4", "PfxFileName": "mycert.pfx", - "SslCertPassword": "test", - "PublicKey": "hv6vvbt2u567syz5labuqnfabsc3zobfwekl4cy3c574n6vkj7sq" + "SslCertPassword": "test" }, { "nodeId": "node2", - "host": "127.0.0.2", - "port": 42066, + "Address": "/ip4/127.0.0.2/tcp/4001/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4", "PfxFileName": "mycert.pfx", - "SslCertPassword": "test", - "PublicKey": "hv6vvbt2u567syz5labuqnfabsc3zobfwekl4cy3c574n6vkj7sq" + "SslCertPassword": "test" } ] } diff --git a/src/Catalyst.TestUtils/Config/testnet.json b/src/Catalyst.TestUtils/Config/testnet.json index 0d1be00dbf..48833bed79 100644 --- a/src/Catalyst.TestUtils/Config/testnet.json +++ b/src/Catalyst.TestUtils/Config/testnet.json @@ -1,7 +1,6 @@ { "CatalystNodeConfiguration": { "Peer": { - "Network": "testnet", "PublicKey": "hv6vvbt2u567syz5labuqnfabsc3zobfwekl4cy3c574n6vkj7sq", "Port": 42076, "BindAddress": "127.0.0.1", diff --git a/src/Catalyst.TestUtils/ContainerProvider.cs b/src/Catalyst.TestUtils/ContainerProvider.cs index 5158ebb379..f3bec846ab 100644 --- a/src/Catalyst.TestUtils/ContainerProvider.cs +++ b/src/Catalyst.TestUtils/ContainerProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,23 +28,31 @@ using Autofac; using Autofac.Configuration; using AutofacSerilogIntegration; +using Catalyst.Abstractions.Cli; using Catalyst.Abstractions.Cryptography; using Catalyst.Abstractions.FileSystem; using Catalyst.Abstractions.Keystore; using Catalyst.Core.Lib; +using Catalyst.Core.Lib.Cli; +using Catalyst.Core.Lib.Cryptography; +using Catalyst.Core.Lib.FileSystem; using Catalyst.Core.Modules.Cryptography.BulletProofs; using Catalyst.Core.Modules.Hashing; using Catalyst.Core.Modules.KeySigner; using Catalyst.Core.Modules.Keystore; +using Catalyst.Core.Modules.Kvm; using DotNetty.Common.Internal.Logging; using Microsoft.Extensions.Configuration; +using Nethermind.Db; +using NSubstitute; +using NUnit.Framework; using Serilog; using Serilog.Core; using Serilog.Events; using Serilog.Extensions.Logging; +using Serilog.Filters; using SharpRepository.Ioc.Autofac; using SharpRepository.Repository; -using Xunit.Abstractions; namespace Catalyst.TestUtils { @@ -52,14 +60,14 @@ public class ContainerProvider : IDisposable { private readonly IEnumerable _configFilesUsed; private readonly IFileSystem _fileSystem; - private readonly ITestOutputHelper _output; + private readonly TestContext _output; private IConfigurationRoot _configRoot; - public ContainerBuilder ContainerBuilder { get; } = new ContainerBuilder(); + public ContainerBuilder ContainerBuilder { get; } = new(); private IContainer _container; public ContainerProvider(IEnumerable configFilesUsed, IFileSystem fileSystem, - ITestOutputHelper output = null) + TestContext output) { _configFilesUsed = configFilesUsed; _fileSystem = fileSystem; @@ -80,7 +88,7 @@ public IConfigurationRoot ConfigurationRoot return _configRoot; } - var configBuilder = new ConfigurationBuilder(); + ConfigurationBuilder configBuilder = new(); _configFilesUsed.ToList().ForEach(f => configBuilder.AddJsonFile(f)); _configRoot = configBuilder.Build(); @@ -96,49 +104,59 @@ public void ConfigureContainerBuilder(bool writeLogsToTestOutput = false, { SocketPortHelper.AlterConfigurationToGetUniquePort(ConfigurationRoot); - var configurationModule = new ConfigurationModule(ConfigurationRoot); + ConfigurationModule configurationModule = new(ConfigurationRoot); ContainerBuilder.RegisterModule(configurationModule); ContainerBuilder.RegisterModule(new CoreLibProvider()); ContainerBuilder.RegisterInstance(ConfigurationRoot).As(); + ContainerBuilder.RegisterType().As(); var repoFactory = RepositoryFactory.BuildSharpRepositoryConfiguation( ConfigurationRoot.GetSection("CatalystNodeConfiguration:PersistenceConfiguration")); ContainerBuilder.RegisterSharpRepository(repoFactory); - var passwordReader = new TestPasswordReader(); + TestPasswordReader passwordReader = new(); ContainerBuilder.RegisterInstance(passwordReader).As(); - var certificateStore = new TestCertificateStore(); + TestCertificateStore certificateStore = new(); ContainerBuilder.RegisterInstance(certificateStore).As(); ContainerBuilder.RegisterInstance(_fileSystem).As(); var keyRegistry = TestKeyRegistry.MockKeyRegistry(); - ContainerBuilder.RegisterInstance(keyRegistry).As(); + ContainerBuilder.RegisterInstance(keyRegistry).As().SingleInstance(); ContainerBuilder.RegisterModule(new BulletProofsModule()); ContainerBuilder.RegisterModule(new KeystoreModule()); ContainerBuilder.RegisterModule(new KeySignerModule()); ContainerBuilder.RegisterModule(new HashingModule()); + ContainerBuilder.RegisterModule(new KvmModule(true)); + + InMemoryStore inMemoryStore = new(); + ContainerBuilder.RegisterInstance(inMemoryStore).As>().SingleInstance(); ConfigureLogging(writeLogsToTestOutput, writeLogsToFile, logDotNettyTraffic); } - private void ConfigureLogging(bool writeLogsToTestOutput, bool writeLogsToFile, bool logDotNettyTraffic = false) + private void ConfigureLogging(bool writeLogsToTestOutput, bool writeLogsToFile, bool logDotNettyTraffic = false, bool logAspTraffic = false) { var loggerConfiguration = new LoggerConfiguration() .ReadFrom.Configuration(ConfigurationRoot).MinimumLevel.Verbose() .Enrich.WithThreadId(); + if (!logAspTraffic) + { + loggerConfiguration = loggerConfiguration.Filter.ByExcluding(Matching.FromSource("Microsoft")); + } + if (writeLogsToTestOutput) { if (_output == null) { throw new NullReferenceException( - $"An instance of {typeof(ITestOutputHelper)} is needed in order to log to the test output"); + $"An instance of {typeof(TestContext)} is needed in order to log to the test output"); } - loggerConfiguration = loggerConfiguration.WriteTo.TestOutput(_output, LogEventLevel, LogOutputTemplate); + loggerConfiguration = loggerConfiguration.WriteTo.NUnitOutput(LogEventLevel, null, null, LogOutputTemplate); } var logFile = Path.Combine(_fileSystem.GetCatalystDataDir().FullName, "Catalyst.Node.log"); @@ -162,7 +180,7 @@ private void ConfigureLogging(bool writeLogsToTestOutput, bool writeLogsToFile, if (writeLogsToFile) { - _output?.WriteLine(logFile); + logger.Information(logFile); } } diff --git a/src/Catalyst.TestUtils/DeltaHelper.cs b/src/Catalyst.TestUtils/DeltaHelper.cs index 77b9bb5b70..7b67f106fa 100644 --- a/src/Catalyst.TestUtils/DeltaHelper.cs +++ b/src/Catalyst.TestUtils/DeltaHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,12 +25,14 @@ using Catalyst.Abstractions.Hashing; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Util; +using Catalyst.Core.Modules.Dfs.Extensions; +using Catalyst.Core.Modules.Kvm; using Catalyst.Protocol.Deltas; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; using Google.Protobuf.WellKnownTypes; -using LibP2P; -using TheDotNetLeague.MultiFormats.MultiBase; +using Lib.P2P; +using MultiFormats; +using Nethermind.Core; namespace Catalyst.TestUtils { @@ -43,7 +45,7 @@ public static Delta GetDelta(IHashProvider hashProvider, DateTime? timestamp = default) { var previousHash = previousDeltaHash ?? - CidHelper.CreateCid(hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32))); + hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32)).ToCid(); var root = merkleRoot ?? ByteUtil.GenerateRandomByteArray(32); var poda = merklePoda ?? ByteUtil.GenerateRandomByteArray(32); var nonNullTimestamp = @@ -63,36 +65,36 @@ public static Delta GetDelta(IHashProvider hashProvider, public static CandidateDeltaBroadcast GetCandidateDelta(IHashProvider hashProvider, Cid previousDeltaHash = null, Cid hash = null, - PeerId producerId = null) + Address producerId = null) { var candidateHash = hash ?? - CidHelper.CreateCid(hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32))); + hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32)).ToCid(); var previousHash = previousDeltaHash ?? - CidHelper.CreateCid(hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32))); + hashProvider.ComputeMultiHash(ByteUtil.GenerateRandomByteArray(32)).ToCid(); var producer = producerId - ?? PeerIdHelper.GetPeerId(ByteUtil.GenerateRandomByteArray(32)); + ?? MultiAddressHelper.GetAddress(ByteUtil.GenerateRandomByteArray(32)).GetPublicKeyBytes().ToKvmAddress(); return new CandidateDeltaBroadcast { Hash = MultiBase.Decode(candidateHash).ToByteString(), PreviousDeltaDfsHash = MultiBase.Decode(previousHash).ToByteString(), - ProducerId = producer + Producer = producer.Bytes.ToByteString() }; } public static FavouriteDeltaBroadcast GetFavouriteDelta(IHashProvider hashProvider, Cid previousDeltaHash = null, Cid hash = null, - PeerId producerId = null, - PeerId voterId = null) + Address producerId = null, + Address voterId = null) { var candidate = GetCandidateDelta(hashProvider, previousDeltaHash, hash, producerId); - var voter = voterId ?? PeerIdHelper.GetPeerId(); + var voter = voterId ?? MultiAddressHelper.GetAddress().GetPublicKeyBytes().ToKvmAddress(); return new FavouriteDeltaBroadcast { Candidate = candidate, - VoterId = voter + Voter = voter.Bytes.ToByteString() }; } } diff --git a/src/Catalyst.TestUtils/DiscoveryHelper.cs b/src/Catalyst.TestUtils/DiscoveryHelper.cs index 8723281759..9aefd1cd18 100644 --- a/src/Catalyst.TestUtils/DiscoveryHelper.cs +++ b/src/Catalyst.TestUtils/DiscoveryHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -40,7 +40,7 @@ using Catalyst.Core.Lib.P2P.Discovery; using Catalyst.Core.Lib.P2P.IO.Messaging.Correlation; using Catalyst.Core.Lib.P2P.Models; -using Catalyst.Core.Lib.P2P.Repository; +using Catalyst.Abstractions.P2P.Repository; using Catalyst.Core.Lib.P2P.ReputationSystem; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.P2P.Discovery.Hastings; @@ -51,6 +51,8 @@ using NSubstitute; using Serilog; using SharpRepository.InMemoryRepository; +using Catalyst.Core.Lib.P2P.Repository; +using MultiFormats; namespace Catalyst.TestUtils { @@ -67,34 +69,34 @@ public override Task DiscoveryAsync() // don't run again for at least 200 milliseconds await Task.Delay(200); } - + // ReSharper disable once FunctionNeverReturns }); } } - public static IHastingsOriginator MockOriginator(PeerId peer = default, + public static IHastingsOriginator MockOriginator(MultiAddress peer = default, INeighbours neighbours = default) { - var memento = new HastingsMemento(peer, neighbours); + HastingsMemento memento = new(peer, neighbours); return new HastingsOriginator(memento); } - public static IHastingsOriginator SubOriginator(PeerId peer = default, + public static IHastingsOriginator SubOriginator(MultiAddress peer = default, INeighbours neighbours = default, ICorrelationId expectedPnr = default) { var subbedOriginator = Substitute.For(); subbedOriginator.Neighbours.Count.Returns(5); - subbedOriginator.Peer.Returns(peer ?? new PeerId()); + subbedOriginator.Peer.Returns(peer ?? MultiAddressHelper.GetAddress()); subbedOriginator.Neighbours.Returns(neighbours ?? Substitute.For()); subbedOriginator.PnrCorrelationId.Returns(expectedPnr ?? CorrelationId.GenerateCorrelationId()); return subbedOriginator; } - public static IHastingsMemento MockSeedState(PeerId ownNode, IPeerSettings peerSettings) + public static IHastingsMemento MockSeedState(MultiAddress ownNode, IPeerSettings peerSettings) { return MockMemento(ownNode, MockDnsClient(peerSettings) .GetSeedNodesFromDnsAsync(peerSettings.SeedServers) @@ -105,13 +107,13 @@ public static IHastingsMemento MockSeedState(PeerId ownNode, IPeerSettings peerS ); } - public static IHastingsOriginator SubSeedOriginator(PeerId ownNode, IPeerSettings peerSettings) + public static IHastingsOriginator SubSeedOriginator(MultiAddress ownNode, IPeerSettings peerSettings) { var m = SubSeedState(ownNode, peerSettings); return SubOriginator(m.Peer, m.Neighbours); } - public static IHastingsMemento SubSeedState(PeerId ownNode, IPeerSettings peerSettings) + public static IHastingsMemento SubSeedState(MultiAddress ownNode, IPeerSettings peerSettings) { var neighbours = MockDnsClient(peerSettings) .GetSeedNodesFromDnsAsync(peerSettings.SeedServers) @@ -129,7 +131,7 @@ public static INeighbours MockNeighbours(int amount = 5, { var neighbours = Enumerable.Range(0, amount).Select(i => new Neighbour( - PeerIdHelper.GetPeerId( + MultiAddressHelper.GetAddress( StringHelper.RandomString() ), stateTypes ?? NeighbourStateTypes.NotContacted, @@ -138,27 +140,27 @@ public static INeighbours MockNeighbours(int amount = 5, return new Neighbours(neighbours); } - public static IDictionary SubContactedNeighbours(int amount = 5) + public static IDictionary SubContactedNeighbours(int amount = 5) { return Enumerable.Range(0, amount) - .Select(i => PeerIdHelper.GetPeerId()) + .Select(i => MultiAddressHelper.GetAddress()) .ToDictionary(v => v, k => Substitute.For()); } - public static IHastingsMemento SubMemento(PeerId identifier = default, + public static IHastingsMemento SubMemento(MultiAddress identifier = default, INeighbours neighbours = default) { var subbedMemento = Substitute.For(); - subbedMemento.Peer.Returns(identifier ?? PeerIdHelper.GetPeerId()); + subbedMemento.Peer.Returns(identifier ?? MultiAddressHelper.GetAddress()); subbedMemento.Neighbours.Returns(neighbours ?? MockNeighbours()); return subbedMemento; } - public static IHastingsMemento MockMemento(PeerId identifier = default, + public static IHastingsMemento MockMemento(MultiAddress identifier = default, INeighbours neighbours = default) { - var peerParam = identifier ?? PeerIdHelper.GetPeerId(StringHelper.RandomString()); + var peerParam = identifier ?? MultiAddressHelper.GetAddress(StringHelper.RandomString()); var neighbourParam = neighbours ?? MockNeighbours(); return new HastingsMemento(peerParam, neighbourParam); } @@ -172,7 +174,7 @@ public static Stack MockMementoHistory(Stack state.Last() .Neighbours .RandomElement() - .PeerId, + .Address, MockNeighbours() ) ); @@ -188,7 +190,7 @@ public static Stack MockMementoHistory(Stack public static IHastingsCareTaker MockCareTaker(IEnumerable history = default) { - var careTaker = new HastingsCareTaker(); + HastingsCareTaker careTaker = new(); history?.ToList().ForEach(m => { careTaker.Add(m); }); @@ -202,7 +204,7 @@ public static IDns MockDnsClient(IPeerSettings settings = default, ILookupClient peerSetting.SeedServers.ToList().ForEach(domain => { MockQueryResponse.CreateFakeLookupResult(domain, - "0x" + PeerIdHelper.GetPeerId( + "0x" + MultiAddressHelper.GetAddress( StringHelper.RandomString(32)).ToString().ToHexUTF8(), lookupClient ?? Substitute.For() ); @@ -218,10 +220,10 @@ public static IPeerRepository MockPeerRepository() public static IPeerClientMessageDto SubDto(Type discoveryMessage, ICorrelationId correlationId = default, - PeerId sender = default) + MultiAddress sender = default) { var dto = Substitute.For(); - dto.Sender.Returns(sender ?? new PeerId()); + dto.Sender.Returns(sender ?? MultiAddressHelper.GetAddress()); dto.CorrelationId.Returns(correlationId ?? Substitute.For()); dto.Message.Returns(Activator.CreateInstance(discoveryMessage)); diff --git a/src/Catalyst.TestUtils/EmbeddedObservableChannel.cs b/src/Catalyst.TestUtils/EmbeddedObservableChannel.cs index 4f388d7dfb..775e998254 100644 --- a/src/Catalyst.TestUtils/EmbeddedObservableChannel.cs +++ b/src/Catalyst.TestUtils/EmbeddedObservableChannel.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,9 +22,8 @@ #endregion using System; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Transport.Channels; -using Catalyst.Core.Lib.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using DotNetty.Transport.Channels.Embedded; @@ -44,7 +43,7 @@ public static IChannelId ToChannelId(this string channelName) } } - public sealed class EmbeddedObservableChannel : IObservableChannel + public sealed class EmbeddedObservableChannel : IObservableChannel { private readonly TestScheduler _testScheduler; private readonly EmbeddedChannel _channel; @@ -54,19 +53,19 @@ public EmbeddedObservableChannel(string channelName) _testScheduler = new TestScheduler(); var channelId = channelName.ToChannelId(); - var observableServiceHandler = new ObservableServiceHandler(_testScheduler); - var embeddedChannel = new EmbeddedChannel(channelId, false, true, observableServiceHandler); + ObservableServiceHandler observableServiceHandler = new(_testScheduler); + EmbeddedChannel embeddedChannel = new(channelId, false, true, observableServiceHandler); _channel = embeddedChannel; MessageStream = observableServiceHandler.MessageStream; } - public void SimulateReceivingMessagesAsync(params object[] messages) + public void SimulateReceivingMessages(params object[] messages) { _channel.WriteInbound(messages); _testScheduler.Start(); } public IChannel Channel => _channel; - public IObservable> MessageStream { get; } + public IObservable MessageStream { get; } } } diff --git a/src/Catalyst.TestUtils/EntryUtils.cs b/src/Catalyst.TestUtils/EntryUtils.cs index 375f2d9d4f..b1de3cb7eb 100644 --- a/src/Catalyst.TestUtils/EntryUtils.cs +++ b/src/Catalyst.TestUtils/EntryUtils.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,10 +24,13 @@ using System; using Catalyst.Abstractions.Cryptography; using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Modules.Kvm; using Catalyst.Protocol.Deltas; using Catalyst.Protocol.Transaction; using Google.Protobuf; using Google.Protobuf.WellKnownTypes; +using Nethermind.Core; +using Nethermind.Core.Crypto; using Nethermind.Core.Extensions; using Nethermind.Dirichlet.Numerics; @@ -35,39 +38,46 @@ namespace Catalyst.TestUtils { public static class EntryUtils { - public static ContractEntry PrepareContractEntry(IPublicKey recipient, IPublicKey sender, UInt256 amount, string dataHex = "0x", ulong nonce = 0) + public static PublicEntry PrepareContractEntry(Address recipient, + Address sender, + UInt256 amount, + string dataHex = "0x", + ulong nonce = 0) { - return new ContractEntry + return new PublicEntry { - Base = new BaseEntry - { - ReceiverPublicKey = recipient == null ? ByteString.Empty : ByteString.CopyFrom(recipient.Bytes), - SenderPublicKey = ByteString.CopyFrom(sender.Bytes), - Nonce = nonce - }, + ReceiverAddress = ByteString.CopyFrom(recipient?.Bytes ?? Bytes.Empty), + SenderAddress = ByteString.CopyFrom(sender?.Bytes ?? Bytes.Empty), + Nonce = nonce, Amount = amount.ToUint256ByteString(), Data = ByteString.CopyFrom(Bytes.FromHexString(dataHex)), GasLimit = 21000, - GasPrice = 0 + GasPrice = 0.ToUint256ByteString() }; } - - public static Delta PrepareSingleContractEntryDelta(IPublicKey recipient, IPublicKey sender, UInt256 amount, string dataHex = "0x", ulong nonce = 0) + + public static Delta PrepareSingleContractEntryDelta(IPublicKey recipient, + IPublicKey sender, + UInt256 amount, + string dataHex = "0x", + ulong nonce = 0) { return new Delta { TimeStamp = Timestamp.FromDateTime(DateTime.UtcNow), - ContractEntries = + StateRoot = ByteString.CopyFrom(Keccak.EmptyTreeHash.Bytes), + PublicEntries = { - PrepareContractEntry(recipient, sender, amount, dataHex, nonce) + PrepareContractEntry(recipient.ToKvmAddress(), sender.ToKvmAddress(), amount, dataHex, nonce) } }; } - + public static Delta PrepareSinglePublicEntryDelta(IPublicKey recipient, IPublicKey sender, UInt256 amount) { return new Delta { + StateRoot = ByteString.CopyFrom(Keccak.EmptyTreeHash.Bytes), TimeStamp = Timestamp.FromDateTime(DateTime.UtcNow), PublicEntries = { @@ -80,12 +90,11 @@ public static PublicEntry PreparePublicEntry(IPublicKey recipient, IPublicKey se { return new PublicEntry { - Base = new BaseEntry - { - ReceiverPublicKey = ByteString.CopyFrom(recipient.Bytes), - SenderPublicKey = ByteString.CopyFrom(sender.Bytes), - }, + ReceiverAddress = recipient.ToKvmAddressByteString(), + SenderAddress = sender.ToKvmAddressByteString(), + Nonce = 0, Amount = amount.ToUint256ByteString(), + GasLimit = 21000 }; } } diff --git a/src/Catalyst.TestUtils/ExceptionAssert.cs b/src/Catalyst.TestUtils/ExceptionAssert.cs new file mode 100644 index 0000000000..29b0d04df1 --- /dev/null +++ b/src/Catalyst.TestUtils/ExceptionAssert.cs @@ -0,0 +1,71 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using NUnit.Framework; +using System; +using System.Linq; + +namespace Catalyst.TestUtils +{ + /// + /// Asserting an . + /// + public static class ExceptionAssert + { + public static T Throws(Action action, string expectedMessage = null) where T : Exception + { + try + { + action(); + } + catch (AggregateException e) + { + var match = e.InnerExceptions.OfType().FirstOrDefault(); + if (match != null) + { + if (expectedMessage != null) + Assert.AreEqual(expectedMessage, match.Message); + return match; + } + + throw; + } + catch (T e) + { + if (expectedMessage != null) + Assert.AreEqual(expectedMessage, e.Message); + return e; + } + + // Assert.Fail("Exception of type {0} should be thrown.", typeof(T)); + + // The compiler doesn't know that Assert.Fail will always throw an exception + return null; + } + + public static Exception Throws(Action action, string expectedMessage = null) + { + return Throws(action, expectedMessage); + } + } +} diff --git a/src/Catalyst.TestUtils/FailingRequestObserver.cs b/src/Catalyst.TestUtils/FailingRequestObserver.cs index 9184be8380..8794d44100 100644 --- a/src/Catalyst.TestUtils/FailingRequestObserver.cs +++ b/src/Catalyst.TestUtils/FailingRequestObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,8 +28,7 @@ using Catalyst.Abstractions.P2P; using Catalyst.Core.Lib.IO.Observers; using Catalyst.Protocol.IPPN; -using Catalyst.Protocol.Peer; -using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.TestUtils @@ -41,11 +40,10 @@ public sealed class FailingRequestObserver : private int _counter; public int Counter => _counter; - public FailingRequestObserver(ILogger logger, IPeerSettings peerSettings) : base(logger, peerSettings) { } + public FailingRequestObserver(ILogger logger, IPeerSettings peerSettings, IPeerClient peerClient) : base(logger, peerSettings, peerClient) { } protected override PeerNeighborsResponse HandleRequest(PeerNeighborsRequest messageDto, - IChannelHandlerContext channelHandlerContext, - PeerId senderPeerId, + MultiAddress sender, ICorrelationId correlationId) { var count = Interlocked.Increment(ref _counter); @@ -54,7 +52,7 @@ protected override PeerNeighborsResponse HandleRequest(PeerNeighborsRequest mess throw new ArgumentException("something went wrong handling the request"); } - return new PeerNeighborsResponse {Peers = {PeerIdHelper.GetPeerId()}}; + return new PeerNeighborsResponse {Peers = {MultiAddressHelper.GetAddress().ToString()}}; } } } diff --git a/src/Catalyst.TestUtils/Fakes/FakeKeySigner.cs b/src/Catalyst.TestUtils/Fakes/FakeKeySigner.cs new file mode 100644 index 0000000000..c23299e9a2 --- /dev/null +++ b/src/Catalyst.TestUtils/Fakes/FakeKeySigner.cs @@ -0,0 +1,52 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.KeySigner; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Types; +using Catalyst.Protocol.Cryptography; + +namespace Catalyst.TestUtils.Fakes +{ + /// + /// A fake that enables to use NSubstitute for faking instances of it. + /// + public abstract class FakeKeySigner : IKeySigner + { + public abstract ICryptoContext CryptoContext { get; } + + // The reimplemented span-based method + ISignature IKeySigner.Sign(ReadOnlySpan data, SigningContext signingContext) => Sign(data.ToArray(), signingContext); + public abstract ISignature Sign(byte[] data, SigningContext signingContext); + + // The reimplemented span-based method + bool IKeySigner.Verify(ISignature signature, ReadOnlySpan data, SigningContext signingContext) => Verify(signature, data.ToArray(), signingContext); + public abstract bool Verify(ISignature signature, byte[] data, SigningContext signingContext); + + public abstract IPrivateKey GetPrivateKey(KeyRegistryTypes keyIdentifier); + + public abstract IPublicKey GetPublicKey(KeyRegistryTypes keyIdentifier); + } +} diff --git a/src/Catalyst.TestUtils/FileHelper.cs b/src/Catalyst.TestUtils/FileHelper.cs index f29e3cba33..419be18dc4 100644 --- a/src/Catalyst.TestUtils/FileHelper.cs +++ b/src/Catalyst.TestUtils/FileHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -50,7 +50,7 @@ public static long GetCrcValue(Stream stream) /// public static long GetCrcValue(string filePath) { - var crc32 = new Crc32(); + Crc32 crc32 = new(); crc32.Update(File.ReadAllBytes(filePath)); return crc32.Value; } @@ -75,7 +75,7 @@ private static string CreateTempFile(byte[] bytes) return filePath; } - using (var fs = new FileStream(filePath, FileMode.Open)) + using (FileStream fs = new(filePath, FileMode.Open)) { fs.Write(bytes, 0, bytes.Length); } @@ -88,7 +88,7 @@ private static string CreateTempFile(byte[] bytes) /// private static long GetCrcValue(this byte[] crcBytes) { - var crc32 = new Crc32(); + Crc32 crc32 = new(); crc32.Update(crcBytes); return crc32.Value; } diff --git a/src/Catalyst.TestUtils/FileSystemBasedTest.cs b/src/Catalyst.TestUtils/FileSystemBasedTest.cs index cfbb526e4a..04a617304b 100644 --- a/src/Catalyst.TestUtils/FileSystemBasedTest.cs +++ b/src/Catalyst.TestUtils/FileSystemBasedTest.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -34,8 +34,8 @@ using Dawn; using FluentAssertions; using NSubstitute; -using Xunit; -using Xunit.Abstractions; +using NUnit.Framework; +using NUnit.Framework.Interfaces; namespace Catalyst.TestUtils { @@ -44,72 +44,73 @@ namespace Catalyst.TestUtils /// A base test class that can be used to offer inheriting tests a folder on which /// to create files, logs, etc. /// - [Trait(Traits.TestType, Traits.IntegrationTest)] + public class FileSystemBasedTest : IDisposable { - protected readonly string CurrentTestName; - protected IFileSystem FileSystem; - protected readonly ITestOutputHelper Output; - private DirectoryInfo _testDirectory; - protected List ConfigFilesUsed { get; } - protected readonly ContainerProvider ContainerProvider; + protected string CurrentTestName; + public IFileSystem FileSystem; + protected TestContext Output; + public DirectoryInfo TestDirectory { set; get; } + protected List ConfigFilesUsed { private set; get; } + protected ContainerProvider ContainerProvider; private DateTime _testStartTime; - protected FileSystemBasedTest(ITestOutputHelper output, + private NetworkType _network; + private IEnumerable _configFilesUsed; + + protected FileSystemBasedTest( IEnumerable configFilesUsed = default, NetworkType network = default) { - Guard.Argument(output, nameof(output)).NotNull(); - Output = output; - var currentTest = Output.GetType().GetField("test", BindingFlags.Instance | BindingFlags.NonPublic) - .GetValue(Output) as ITest; + _network = network; + _configFilesUsed = configFilesUsed; + } - if (currentTest == null) - { - throw new ArgumentNullException( - $"Failed to reflect current test as {nameof(ITest)} from {nameof(output)}"); - } + public virtual void Setup(TestContext output) + { + Guard.Argument(output, nameof(output)).NotNull(); - CurrentTestName = currentTest.TestCase.TestMethod.Method.Name; + Output = output; + CurrentTestName = Output.Test.MethodName; CreateUniqueTestDirectory(); - + ConfigFilesUsed = new List { Path.Combine(Constants.ConfigSubFolder, Constants.SerilogJsonConfigFile), - Path.Combine(Constants.ConfigSubFolder, Constants.NetworkConfigFile(network == default ? NetworkType.Devnet : network)) + Path.Combine(Constants.ConfigSubFolder, Constants.NetworkConfigFile(_network == default ? NetworkType.Devnet : _network)) }; - configFilesUsed?.ToList().ForEach(config => + _configFilesUsed?.ToList().ForEach(config => { - ConfigFilesUsed.Add(config); + ConfigFilesUsed.Add(config); }); ContainerProvider = new ContainerProvider(ConfigFilesUsed, FileSystem, Output); ContainerProvider.ConfigureContainerBuilder(true, true); - Output.WriteLine("test running in folder {0}", _testDirectory.FullName); + TestContext.WriteLine("test running in folder {0}", TestDirectory.FullName); } protected void CreateUniqueTestDirectory() { var testStartTime = DateTime.Now; _testStartTime = testStartTime; - _testDirectory = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, + TestDirectory = new DirectoryInfo(Path.Combine(Environment.CurrentDirectory, //get a unique folder for this run CurrentTestName + $"_{_testStartTime:yyMMddHHmmssffff}")); - _testDirectory.Exists.Should().BeFalse(); - _testDirectory.Create(); + TestDirectory.Exists.Should().BeFalse(); + TestDirectory.Create(); FileSystem = GetFileSystemStub(); } private IFileSystem GetFileSystemStub() { - var fileSystem = Substitute.ForPartsOf(); - fileSystem.GetCatalystDataDir().Returns(_testDirectory); + var fileSystem = Substitute.For(); + fileSystem.GetCatalystDataDir().Returns(TestDirectory); return fileSystem; } @@ -117,14 +118,14 @@ private IFileSystem GetFileSystemStub() protected virtual void Dispose(bool disposing) { - if (!disposing || _testDirectory?.Parent == null) + if (!disposing || TestDirectory?.Parent == null) { return; } - var regex = new Regex(CurrentTestName + @"_(?[\d]{16})"); + Regex regex = new(CurrentTestName + @"_(?[\d]{16})"); - var oldDirectories = _testDirectory.Parent.EnumerateDirectories() + var oldDirectories = TestDirectory.Parent.EnumerateDirectories() .Where(d => { var matches = regex.Matches(d.Name); diff --git a/src/Catalyst.TestUtils/FluentAssertionsExtensions.cs b/src/Catalyst.TestUtils/FluentAssertionsExtensions.cs index b285d4b886..abd3a4b60b 100644 --- a/src/Catalyst.TestUtils/FluentAssertionsExtensions.cs +++ b/src/Catalyst.TestUtils/FluentAssertionsExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using FluentAssertions; using FluentAssertions.Collections; using FluentAssertions.Execution; -using Xunit; +using NUnit.Framework; namespace Catalyst.TestUtils { @@ -63,7 +63,7 @@ private static AndConstraint> NotBeInOrder>(constraint); } - throw new AssertionFailedException(string.Format(errorMessage, string.Join(",", enumerable))); + throw new AssertionException(string.Format(errorMessage, string.Join(",", enumerable))); } public static AndConstraint> NotBeInDescendingOrder(this GenericCollectionAssertions constraint, @@ -91,20 +91,21 @@ private sealed class StringWrapper internal string StringValue { get; } } - private readonly StringWrapper[] _stringsInAscendingOrder; - private readonly IEnumerable _stringsInDescendingOrder; - private readonly IEnumerable _stringsInRandomOrder; + private StringWrapper[] _stringsInAscendingOrder; + private IEnumerable _stringsInDescendingOrder; + private IEnumerable _stringsInRandomOrder; - public FluentAssertionsExtensionsTests() + [SetUp] + public void Init() { - _stringsInAscendingOrder = new[] {"A", "a", "b", "d", "r", "Z"} - .Select(x => new StringWrapper(x)).ToArray(); + _stringsInAscendingOrder = new[] { "A", "a", "b", "d", "r", "Z" } + .Select(x => new StringWrapper(x)).ToArray(); _stringsInDescendingOrder = _stringsInAscendingOrder.Reverse(); - _stringsInRandomOrder = new[] {"A", "a", "Z", "b", "r", "d"} + _stringsInRandomOrder = new[] { "A", "a", "Z", "b", "r", "d" } .Select(x => new StringWrapper(x)); } - [Fact] + [Test] public void NotBeInDescendingOrder_should_only_throw_when_in_descending_order() { _stringsInAscendingOrder.Should() @@ -113,23 +114,23 @@ public void NotBeInDescendingOrder_should_only_throw_when_in_descending_order() .NotBeInDescendingOrder(x => x.StringValue, StringComparer.InvariantCultureIgnoreCase); new Action(() => _stringsInDescendingOrder.Should() .NotBeInDescendingOrder(x => x.StringValue, StringComparer.InvariantCultureIgnoreCase)) - .Should().Throw(); + .Should().Throw(); } - [Fact] + [Test] public void NotBeInDescendingOrder_should_use_the_passed_in_comparer() { new Action(() => _stringsInDescendingOrder.Should() .NotBeInDescendingOrder(x => x.StringValue, StringComparer.InvariantCultureIgnoreCase)) - .Should().Throw(); + .Should().Throw(); _stringsInDescendingOrder.Should() .NotBeInDescendingOrder(x => x.StringValue, StringComparer.InvariantCulture); } - [Fact] + [Test] public void NotBeInAscendingOrder_should_only_throw_when_in_ascending_order() { _stringsInDescendingOrder.Should() @@ -138,32 +139,20 @@ public void NotBeInAscendingOrder_should_only_throw_when_in_ascending_order() .NotBeInAscendingOrder(x => x.StringValue, StringComparer.InvariantCultureIgnoreCase); new Action(() => _stringsInAscendingOrder.Should() .NotBeInAscendingOrder(x => x.StringValue, StringComparer.InvariantCultureIgnoreCase)) - .Should().Throw(); + .Should().Throw(); } - [Fact] + [Test] public void NotBeInAscendingOrder_should_use_the_passed_in_comparer() { new Action(() => _stringsInAscendingOrder.Should() .NotBeInAscendingOrder(x => x.StringValue, StringComparer.InvariantCultureIgnoreCase)) - .Should().Throw(); + .Should().Throw(); _stringsInAscendingOrder.Should() .NotBeInAscendingOrder(x => x.StringValue, StringComparer.InvariantCulture); } - - [Fact] - public void NotBeIn_ascending_or_descending_Order_should_be_true_on_empty() - { - new List().Should() - .NotBeInAscendingOrder(x => x.StringValue, - StringComparer.InvariantCulture); - - new List().Should() - .NotBeInDescendingOrder(x => x.StringValue, - StringComparer.InvariantCulture); - } } } diff --git a/src/Catalyst.TestUtils/IComponentWithRepository.cs b/src/Catalyst.TestUtils/IComponentWithRepository.cs index 490c8da0fd..bdd29b04bd 100644 --- a/src/Catalyst.TestUtils/IComponentWithRepository.cs +++ b/src/Catalyst.TestUtils/IComponentWithRepository.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.TestUtils/MessageStreamHelper.cs b/src/Catalyst.TestUtils/MessageStreamHelper.cs index 435fbfcfef..b92d7a5731 100644 --- a/src/Catalyst.TestUtils/MessageStreamHelper.cs +++ b/src/Catalyst.TestUtils/MessageStreamHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,65 +24,68 @@ using System; using System.Linq; using System.Reactive.Linq; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Messaging.Correlation; -using Catalyst.Core.Lib.IO.Messaging.Dto; using Catalyst.Core.Lib.IO.Observers; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.IO.Messaging.Dto; using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using Google.Protobuf; using Microsoft.Reactive.Testing; -using NSubstitute; namespace Catalyst.TestUtils { public static class MessageStreamHelper { + public static void SendToHandler(this ProtocolMessage message, + MessageObserverBase handler) + { + handler.OnNext(message); + } + public static void SendToHandler(this ProtocolMessage messages, IChannelHandlerContext fakeContext, - MessageObserverBase handler) + MessageObserverBase> handler) { handler.OnNext(CreateChanneledMessage(fakeContext, messages)); } - //Force test scheduler for testing streams public static IObservable> CreateStreamWithMessage(IChannelHandlerContext fakeContext, - TestScheduler testScheduler, - ProtocolMessage response) + TestScheduler testScheduler, + ProtocolMessage response) { - var channeledAny = new ObserverDto(fakeContext, response); - var messageStream = new[] {channeledAny}.ToObservable(testScheduler); + ObserverDto channeledAny = new(fakeContext, response); + var messageStream = new[] { channeledAny }.ToObservable(testScheduler); return messageStream; } - public static IObservable> CreateStreamWithMessages(TestScheduler testScheduler, - params T[] messages) - where T : IMessage, IMessage + private static ObserverDto CreateChanneledMessage(IChannelHandlerContext fakeContext, + ProtocolMessage responseMessage) { - var protoMessages = messages.Select(m => - m.ToProtocolMessage(PeerIdHelper.GetPeerId(), CorrelationId.GenerateCorrelationId())); - - var context = Substitute.For(); + return new ObserverDto(fakeContext, responseMessage); + } - return CreateStreamWithMessages(context, testScheduler, protoMessages.ToArray()); + //Force test scheduler for testing streams + public static IObservable CreateStreamWithMessage(TestScheduler testScheduler, + ProtocolMessage response) + { + return new[] { response }.ToObservable(testScheduler); } - public static IObservable> CreateStreamWithMessages(IChannelHandlerContext fakeContext, - TestScheduler testScheduler, - params ProtocolMessage[] responseMessages) + public static IObservable CreateStreamWithMessages(TestScheduler testScheduler, + params T[] messages) + where T : IMessage, IMessage { - var stream = responseMessages - .Select(message => new ObserverDto(fakeContext, message)); + var protoMessages = messages.Select(m => m.ToProtocolMessage(MultiAddressHelper.GetAddress(), CorrelationId.GenerateCorrelationId())); - var messageStream = stream.ToObservable(testScheduler); - return messageStream; + return CreateStreamWithMessages(testScheduler, protoMessages.ToArray()); } - private static ObserverDto CreateChanneledMessage(IChannelHandlerContext fakeContext, - ProtocolMessage responseMessage) + public static IObservable CreateStreamWithMessages(TestScheduler testScheduler, + params ProtocolMessage[] responseMessages) { - return new ObserverDto(fakeContext, responseMessage); + return responseMessages.ToObservable(testScheduler);; } } } diff --git a/src/Catalyst.TestUtils/MockQueryResponse.cs b/src/Catalyst.TestUtils/MockQueryResponse.cs index f901a3db5d..3c66d14346 100644 --- a/src/Catalyst.TestUtils/MockQueryResponse.cs +++ b/src/Catalyst.TestUtils/MockQueryResponse.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.TestUtils/MultiAddressHelper.cs b/src/Catalyst.TestUtils/MultiAddressHelper.cs new file mode 100644 index 0000000000..b0b69c8f64 --- /dev/null +++ b/src/Catalyst.TestUtils/MultiAddressHelper.cs @@ -0,0 +1,85 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Net; +using System.Text; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.P2P; +using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Modules.Cryptography.BulletProofs; +using MultiFormats; +using NSubstitute; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Catalyst.TestUtils +{ + public static class MultiAddressHelper + { + private static ICryptoContext ffiWrapper = new FfiWrapper(); + public static MultiAddress GetAddress(byte[] publicKey = null, + IPAddress ipAddress = null, + int port = 12345) + { + if (publicKey == null || publicKey.Length < ffiWrapper.PrivateKeyLength) + { + var g = GeneratorUtilities.GetKeyPairGenerator("Ed25519"); + + var random = SecureRandom.GetInstance("SHA1PRNG", false); + if (publicKey != null) + { + random.SetSeed(publicKey); + } + + g.Init(new Ed25519KeyGenerationParameters(random)); + var keyPair = g.GenerateKeyPair(); + publicKey = ((Ed25519PublicKeyParameters) keyPair.Public).GetEncoded(); + } + + MultiAddress address = new($"/ip4/{ipAddress ?? IPAddress.Loopback}/tcp/{port}/ipfs/{publicKey.ToPeerId()}"); + return address; + } + + public static MultiAddress GetAddress(string publicKeySeed, + IPAddress ipAddress = null, + int port = 12345) + { + return GetAddress(Encoding.UTF8.GetBytes(publicKeySeed), ipAddress, port); + } + + public static MultiAddress GetAddress(string publicKey, string ipAddress, int port) + { + return GetAddress(publicKey, IPAddress.Parse(ipAddress), port); + } + + public static IPeerSettings ToSubstitutedPeerSettings(this MultiAddress address) + { + var peerSettings = Substitute.For(); + peerSettings.Address.Returns(address); + peerSettings.BindAddress.Returns(IPAddress.Parse(address.GetIpAddress().ToString())); + peerSettings.Port.Returns((int) address.GetPort()); + peerSettings.PublicKey.Returns(address.GetPublicKey()); + return peerSettings; + } + } +} diff --git a/src/Catalyst.TestUtils/ObservableHelpers.cs b/src/Catalyst.TestUtils/ObservableHelpers.cs index 2e914a7ddc..68ad52a530 100644 --- a/src/Catalyst.TestUtils/ObservableHelpers.cs +++ b/src/Catalyst.TestUtils/ObservableHelpers.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,9 +23,9 @@ using System; using System.Reactive.Linq; -using Catalyst.Abstractions.IO.Messaging.Dto; -using Catalyst.Abstractions.IO.Transport.Channels; -using Catalyst.Core.Lib.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Messaging.Dto; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using NSubstitute; @@ -34,11 +34,19 @@ namespace Catalyst.TestUtils { public static class ObservableHelpers { - public static IObservableChannel MockObservableChannel(IObservable> replaySubject) + public static IRpcObservableChannel MockRpcObservableChannel(IObservable> replaySubject) { var mockChannel = Substitute.For(); var mockEventStream = replaySubject.AsObservable(); - var observableChannel = new ObservableChannel(mockEventStream, mockChannel); + RpcObservableChannel observableChannel = new(mockEventStream, mockChannel); + return observableChannel; + } + + public static IP2PObservableChannel MockP2PObservableChannel(IObservable replaySubject) + { + var mockChannel = Substitute.For(); + var mockEventStream = replaySubject.AsObservable(); + P2PObservableChannel observableChannel = new(mockEventStream, mockChannel); return observableChannel; } } diff --git a/src/Catalyst.TestUtils/PeerIdHelper.cs b/src/Catalyst.TestUtils/PeerIdHelper.cs deleted file mode 100644 index 4d5a6e828c..0000000000 --- a/src/Catalyst.TestUtils/PeerIdHelper.cs +++ /dev/null @@ -1,77 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System.Linq; -using System.Net; -using System.Text; -using Catalyst.Abstractions.P2P; -using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Network; -using Catalyst.Core.Lib.Util; -using Catalyst.Core.Modules.Cryptography.BulletProofs; -using Catalyst.Protocol.Peer; -using NSubstitute; - -namespace Catalyst.TestUtils -{ - public static class PeerIdHelper - { - public static PeerId GetPeerId(byte[] publicKey = null, - IPAddress ipAddress = null, - int port = 12345) - { - var peerIdentifier = new PeerId - { - PublicKey = (publicKey ?? new byte[32]).ToByteString(), - Ip = (ipAddress ?? IPAddress.Loopback).To16Bytes().ToByteString(), - Port = (ushort) port - }; - return peerIdentifier; - } - - public static PeerId GetPeerId(string publicKeySeed, - IPAddress ipAddress = null, - int port = 12345) - { - var publicKeyBytes = Encoding.UTF8.GetBytes(publicKeySeed) - .Concat(Enumerable.Repeat(default(byte), new FfiWrapper().PublicKeyLength)) - .Take(new FfiWrapper().PublicKeyLength).ToArray(); - return GetPeerId(publicKeyBytes, ipAddress, port); - } - - public static PeerId GetPeerId(string publicKey, string ipAddress, int port) - { - return GetPeerId(publicKey, IPAddress.Parse(ipAddress), port); - } - - public static IPeerSettings ToSubstitutedPeerSettings(this PeerId peerId) - { - var peerSettings = Substitute.For(); - peerSettings.PeerId.Returns(peerId); - peerSettings.BindAddress.Returns(peerId.IpAddress); - peerSettings.Port.Returns((int) peerId.Port); - peerSettings.PublicKey.Returns(peerId.PublicKey.KeyToString()); - return peerSettings; - } - } -} diff --git a/src/Catalyst.TestUtils/PeerSettingsHelper.cs b/src/Catalyst.TestUtils/PeerSettingsHelper.cs index 2f29d92318..3ae11dcef6 100644 --- a/src/Catalyst.TestUtils/PeerSettingsHelper.cs +++ b/src/Catalyst.TestUtils/PeerSettingsHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -36,10 +36,11 @@ public static class PeerSettingsHelper { public static IPeerSettings TestPeerSettings(byte[] publicKey = default, int port = 42069) { - var finalPublicKey = publicKey ?? ByteUtil.GenerateRandomByteArray(new FfiWrapper().PublicKeyLength); + var peerId = MultiAddressHelper.GetAddress(publicKey); + var finalPublicKey = peerId.GetPublicKey(); var peerSettings = Substitute.For(); peerSettings.NetworkType.Returns(NetworkType.Devnet); - peerSettings.PublicKey.Returns(finalPublicKey.KeyToString()); + peerSettings.PublicKey.Returns(finalPublicKey); peerSettings.Port.Returns(port); peerSettings.PayoutAddress.Returns("my_pay_out_address"); peerSettings.BindAddress.Returns(IPAddress.Loopback); @@ -51,7 +52,8 @@ public static IPeerSettings TestPeerSettings(byte[] publicKey = default, int por "seed4.catalystnetwork.io", "seed5.catalystnetwork.io" }); - peerSettings.PeerId.Returns(finalPublicKey.BuildPeerIdFromPublicKey(IPAddress.Loopback, port)); + + peerSettings.Address.Returns(peerId); return peerSettings; } } diff --git a/src/Catalyst.TestUtils/PoaTestNode.cs b/src/Catalyst.TestUtils/PoaTestNode.cs new file mode 100644 index 0000000000..2a34815d9c --- /dev/null +++ b/src/Catalyst.TestUtils/PoaTestNode.cs @@ -0,0 +1,301 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Autofac; +using Autofac.Core; +using Catalyst.Abstractions; +using Catalyst.Abstractions.Cli; +using Catalyst.Abstractions.Consensus; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.DAO; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.FileSystem; +using Catalyst.Abstractions.IO.Observers; +using Catalyst.Abstractions.Ledger.Models; +using Catalyst.Abstractions.Mempool; +using Catalyst.Abstractions.P2P; +using Catalyst.Abstractions.P2P.Discovery; +using Catalyst.Core.Lib.Config; +using Catalyst.Core.Lib.DAO.Transaction; +using Catalyst.Core.Lib.P2P.Models; +using Catalyst.Abstractions.P2P.Repository; +using Catalyst.Core.Lib; +using Catalyst.Core.Lib.Cli; +using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Modules.Ledger.Repository; +using Catalyst.Core.Modules.Mempool; +using Catalyst.Core.Modules.Mempool.Repositories; +using Catalyst.Protocol.Network; +using NSubstitute; +using NUnit.Framework; +using SharpRepository.InMemoryRepository; +using Catalyst.Core.Lib.P2P.Repository; +using Nethermind.Db; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Modules.Authentication; +using Catalyst.Core.Modules.Consensus; +using Catalyst.Core.Modules.Cryptography.BulletProofs; +using Catalyst.Core.Modules.Dfs; +using Catalyst.Core.Modules.Hashing; +using Catalyst.Core.Modules.KeySigner; +using Catalyst.Core.Modules.Keystore; +using Catalyst.Core.Modules.Kvm; +using Catalyst.Core.Modules.Ledger; +using Catalyst.Core.Modules.P2P.Discovery.Hastings; +using Catalyst.Core.Modules.Sync; +using Catalyst.Core.Modules.Web3; +using Catalyst.Modules.POA.Consensus; +using Catalyst.Modules.POA.P2P; +using Catalyst.Node.POA.CE; +using SharpRepository.Repository; +using Catalyst.Abstractions.Dfs.CoreApi; +using Newtonsoft.Json.Linq; +using MultiFormats; +using Catalyst.Core.Abstractions.Sync; +using Catalyst.Abstractions.Sync.Interfaces; +using System.Net; +using Catalyst.Core.Modules.Web3.Options; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; +using Catalyst.Modules.Network.LibP2P; + +namespace Catalyst.TestUtils +{ + public sealed class PoaTestNode : ICatalystNode, IDisposable + { + public delegate void PeerActivedHandler(MultiAddress peerAddress); + public event PeerActivedHandler PeerActive; + + private IDfsService _dfsService; + private readonly IMempool _memPool; + private readonly ICatalystNode _node; + private readonly DirectoryInfo _nodeDirectory; + private readonly IPeerRepository _peerRepository; + private readonly ILifetimeScope _scope; + private readonly ContainerProvider _containerProvider; + private readonly IDeltaByNumberRepository _deltaByNumber; + private readonly bool _isSynchronized; + private Lib.P2P.Peer _localPeer; + + public ContainerProvider GetContainerProvider() + { + return _containerProvider; + } + + public PoaTestNode(int nodeNumber, bool isSynchronized, + IFileSystem parentTestFileSystem) + { + NodeNumber = nodeNumber; + _isSynchronized = isSynchronized; + + _nodeDirectory = parentTestFileSystem.GetCatalystDataDir(); + + _memPool = new Mempool(new MempoolService(new InMemoryRepository())); + _peerRepository = new PeerRepository(new InMemoryRepository()); + + _deltaByNumber = new DeltaByNumberRepository(new InMemoryRepository()); + + _containerProvider = new ContainerProvider(new[] + { + Constants.NetworkConfigFile(NetworkType.Devnet), + Constants.SerilogJsonConfigFile + } + .Select(f => Path.Combine(Constants.ConfigSubFolder, f)), parentTestFileSystem, TestContext.CurrentContext); + + RegisterNodeDependencies(_containerProvider.ContainerBuilder, + excludedModules: new List + { + typeof(ApiModule) + } + ); + _containerProvider.ConfigureContainerBuilder(true, true); + OverrideContainerBuilderRegistrations(); + + _scope = _containerProvider.Container.BeginLifetimeScope(NodeNumber); + _node = _scope.Resolve(); + } + + public static void RegisterNodeDependencies(ContainerBuilder containerBuilder, + List extraModuleInstances = default, + List excludedModules = default) + { + // core modules + containerBuilder.RegisterType().As(); + containerBuilder.RegisterType().As(); + containerBuilder.RegisterType().As(); + + // message handlers + containerBuilder.RegisterAssemblyTypes(typeof(CoreLibProvider).Assembly) + .AssignableTo().As(); + + // DAO MapperInitialisers + containerBuilder.RegisterAssemblyTypes(typeof(CoreLibProvider).Assembly) + .AssignableTo().As(); + containerBuilder.RegisterType().As() + .SingleInstance(); + + var modulesToRegister = DefaultModulesByTypes + .Where(p => excludedModules == null || !excludedModules.Contains(p.Key)) + .Select(p => p.Value()) + .Concat(extraModuleInstances ?? new List()); + + foreach (var module in modulesToRegister) + { + containerBuilder.RegisterModule(module); + } + } + + private static readonly Dictionary> DefaultModulesByTypes = + new Dictionary> + { + {typeof(CoreLibProvider), () => new CoreLibProvider()}, + {typeof(MempoolModule), () => new MempoolModule()}, + {typeof(ConsensusModule), () => new ConsensusModule()}, + {typeof(SynchroniserModule), () => new SynchroniserModule()}, + {typeof(KvmModule), () => new KvmModule()}, + {typeof(LedgerModule), () => new LedgerModule()}, + {typeof(HashingModule), () => new HashingModule()}, + {typeof(DiscoveryHastingModule), () => new DiscoveryHastingModule()}, + {typeof(BulletProofsModule), () => new BulletProofsModule()}, + {typeof(KeystoreModule), () => new KeystoreModule()}, + {typeof(KeySignerModule), () => new KeySignerModule()}, + {typeof(DfsModule), () => new DfsModule()}, + {typeof(AuthenticationModule), () => new AuthenticationModule()}, + { + typeof(ApiModule), + () => new ApiModule(new HttpOptions(new IPEndPoint(IPAddress.Any, 5005)), new HttpsOptions(new IPEndPoint(IPAddress.Any, 2053), "cert.pfx"), new List {"Catalyst.Core.Modules.Web3", "Catalyst.Core.Modules.Dfs"}) + }, + {typeof(PoaConsensusModule), () => new PoaConsensusModule()}, + {typeof(PoaP2PModule), () => new PoaP2PModule()}, + {typeof(LibP2PNetworkModule), () => new LibP2PNetworkModule()} + }; + + private int NodeNumber { get; } + + public IConsensus Consensus => _node.Consensus; + + public async Task RunAsync(CancellationToken cancellationSourceToken) + { + _localPeer = _scope.Resolve(); + var synchronizer = _scope.Resolve(); + var peerSettings = _scope.Resolve(); + var config = _scope.Resolve(); + _dfsService = _scope.Resolve(); + + await _dfsService.StartAsync().ConfigureAwait(false); + + PeerActive.Invoke(peerSettings.Address); + + await StartSocketsAsync().ConfigureAwait(false); + + var peers = await _dfsService.SwarmApi.PeersAsync().ConfigureAwait(false); + while (peers.Count() < 2) + { + peers = await _dfsService.SwarmApi.PeersAsync().ConfigureAwait(false); + await Task.Delay(100).ConfigureAwait(false); + } + + if (!_isSynchronized) + { + await synchronizer.StartAsync().ConfigureAwait(false); + synchronizer.SyncCompleted.Subscribe((x) => + { + Consensus.StartProducing(); + }); + } + else + { + Consensus.StartProducing(); + } + + do + { + await Task.Delay(300, cancellationSourceToken).ConfigureAwait(false); + } while (!cancellationSourceToken.IsCancellationRequested); + } + + public async Task RegisterPeerAddressAsync(MultiAddress multiAddress) + { + _peerRepository.Add(new Peer + { + Address = multiAddress, + IsPoaNode = true, + LastSeen = DateTime.UtcNow + }); + + if (_localPeer.Id != multiAddress.PeerId) + { + await _dfsService.SwarmService.ConnectAsync(multiAddress).ConfigureAwait(false); + } + } + + public async Task StartSocketsAsync() { await _node.StartSocketsAsync(); } + + public void Dispose() { Dispose(true); } + + private void OverrideContainerBuilderRegistrations() + { + var builder = _containerProvider.ContainerBuilder; + + builder.RegisterInstance(new SyncState { IsSynchronized = _isSynchronized }).As(); + builder.RegisterInstance(_deltaByNumber).As(); + builder.RegisterInstance(new MemDb()).As().SingleInstance(); + builder.RegisterInstance(new StateDb()).As().SingleInstance(); + builder.RegisterInstance(new InMemoryRepository()).As>().SingleInstance(); + builder.RegisterType>().As>().SingleInstance(); + builder.RegisterInstance(new InMemoryRepository()) + .AsImplementedInterfaces(); + builder.RegisterInstance(new InMemoryRepository()) + .AsImplementedInterfaces(); + + _containerProvider.ContainerBuilder.RegisterInstance(new TestPasswordReader()).As(); + _containerProvider.ContainerBuilder.RegisterInstance(_memPool).As>(); + _containerProvider.ContainerBuilder.RegisterInstance(_peerRepository).As(); + _containerProvider.ContainerBuilder.RegisterType().As() + .WithParameter("rootPath", _nodeDirectory.FullName); + _containerProvider.ContainerBuilder.RegisterInstance(Substitute.For()).As(); + + var config = Substitute.For(); + var swarm = JToken.FromObject(new List { $"/ip4/0.0.0.0/tcp/410{NodeNumber}" }); + config.GetAsync("Addresses.Swarm").Returns(swarm); + _containerProvider.ContainerBuilder.RegisterInstance(config).As(); + } + + private void Dispose(bool disposing) + { + if (!disposing) + { + return; + } + + _scope?.Dispose(); + _peerRepository?.Dispose(); + _containerProvider?.Dispose(); + } + } +} diff --git a/src/Catalyst.TestUtils/Protocol/AddressHelper.cs b/src/Catalyst.TestUtils/Protocol/AddressHelper.cs index 48d9b43efb..1290b908d9 100644 --- a/src/Catalyst.TestUtils/Protocol/AddressHelper.cs +++ b/src/Catalyst.TestUtils/Protocol/AddressHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ using Catalyst.Core.Modules.Hashing; using Catalyst.Protocol.Account; using Catalyst.Protocol.Network; -using TheDotNetLeague.MultiFormats.MultiHash; +using MultiFormats.Registry; namespace Catalyst.TestUtils.Protocol { @@ -38,7 +38,7 @@ public static Address GetAddress(string publicKeySeed = "publicKey", NetworkType networkType = NetworkType.Devnet, AccountType accountType = AccountType.PublicAccount) { - var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("blake2b-256")); + var hashProvider = new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256")); var address = new Address { diff --git a/src/Catalyst.TestUtils/Protocol/DevNetPeerSigningContext.cs b/src/Catalyst.TestUtils/Protocol/DevNetPeerSigningContext.cs index fdf623b2c4..35431a457e 100644 --- a/src/Catalyst.TestUtils/Protocol/DevNetPeerSigningContext.cs +++ b/src/Catalyst.TestUtils/Protocol/DevNetPeerSigningContext.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.TestUtils/Protocol/SignatureHelper.cs b/src/Catalyst.TestUtils/Protocol/SignatureHelper.cs index 19978a0758..fbb28ec7da 100644 --- a/src/Catalyst.TestUtils/Protocol/SignatureHelper.cs +++ b/src/Catalyst.TestUtils/Protocol/SignatureHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.TestUtils/ProtocolHelpers/BaseEntryHelper.cs b/src/Catalyst.TestUtils/ProtocolHelpers/BaseEntryHelper.cs deleted file mode 100644 index 138fa5d5d5..0000000000 --- a/src/Catalyst.TestUtils/ProtocolHelpers/BaseEntryHelper.cs +++ /dev/null @@ -1,54 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using Catalyst.Core.Lib.DAO; -using Catalyst.Core.Lib.Extensions; -using Catalyst.Core.Lib.Util; -using Catalyst.Protocol.Transaction; -using Nethermind.Core.Extensions; - -namespace Catalyst.TestUtils.ProtocolHelpers -{ - public static class BaseEntryHelper - { - private static readonly IMapperProvider MapperProvider = new TestMapperProvider(); - - public static BaseEntry GetBaseEntry() - { - var fees = new Random().Next(78588446).ToByteArray(new Bytes.Endianness()); - - return new BaseEntry - { - TransactionFees = fees.ToByteString(), - ReceiverPublicKey = ByteUtil.GenerateRandomByteArray(32).ToByteString(), - SenderPublicKey = ByteUtil.GenerateRandomByteArray(32).ToByteString(), - }; - } - - public static BaseEntryDao GetBaseEntryDao() - { - return GetBaseEntry().ToDao(MapperProvider); - } - } -} diff --git a/src/Catalyst.TestUtils/ProtocolHelpers/ConfidentialEntryHelper.cs b/src/Catalyst.TestUtils/ProtocolHelpers/ConfidentialEntryHelper.cs index 389d2bba01..e80fe4b4f4 100644 --- a/src/Catalyst.TestUtils/ProtocolHelpers/ConfidentialEntryHelper.cs +++ b/src/Catalyst.TestUtils/ProtocolHelpers/ConfidentialEntryHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,12 +21,15 @@ #endregion +using System; using System.Collections.Generic; using System.Linq; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Util; using Catalyst.Protocol.Transaction; +using Nethermind.Core.Extensions; namespace Catalyst.TestUtils.ProtocolHelpers { @@ -36,11 +39,14 @@ public static class ConfidentialEntryHelper public static ConfidentialEntry GetConfidentialEntry() { + var fees = new Random().Next(78588446).ToByteArray(); return new ConfidentialEntry { PedersenCommitment = ByteUtil.GenerateRandomByteArray(32).ToByteString(), - RangeProof = ByteUtil.GenerateRandomByteArray(256).ToByteString(), - Base = BaseEntryHelper.GetBaseEntry() + RangeProof = new RangeProof(), + TransactionFees = fees.ToByteString(), + ReceiverPublicKey = ByteUtil.GenerateRandomByteArray(32).ToByteString(), + SenderPublicKey = ByteUtil.GenerateRandomByteArray(32).ToByteString(), }; } diff --git a/src/Catalyst.TestUtils/ProtocolHelpers/ContractEntryHelper.cs b/src/Catalyst.TestUtils/ProtocolHelpers/ContractEntryHelper.cs index 3b7de94cac..b78f7bde69 100644 --- a/src/Catalyst.TestUtils/ProtocolHelpers/ContractEntryHelper.cs +++ b/src/Catalyst.TestUtils/ProtocolHelpers/ContractEntryHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,6 +25,7 @@ using System.Collections.Generic; using System.Linq; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.Util; using Catalyst.Protocol.Transaction; @@ -36,33 +37,35 @@ public static class ContractEntryHelper { private static readonly IMapperProvider MapperProvider = new TestMapperProvider(); - public static ContractEntry GetContractEntry() + public static PublicEntry GetContractEntry() { - var amount = new Random().Next(78588446).ToByteArray(new Bytes.Endianness()); + var amount = new Random().Next(78588446).ToByteArray(); + var fees = new Random().Next(78588446).ToByteArray(); - return new ContractEntry + return new PublicEntry { Data = ByteUtil.GenerateRandomByteArray(32).ToByteString(), Amount = amount.ToByteString(), - Base = BaseEntryHelper.GetBaseEntry() + ReceiverAddress = ByteUtil.GenerateRandomByteArray(32).ToByteString(), + SenderAddress = ByteUtil.GenerateRandomByteArray(32).ToByteString(), }; } - public static IEnumerable GetContractEntries(int count) + public static IEnumerable GetContractEntries(int count) { - var contractList = new List(); + var contractList = new List(); Enumerable.Range(0, count).ToList().ForEach(i => { - contractList.Add(ContractEntryHelper.GetContractEntry()); + contractList.Add(GetContractEntry()); }); return contractList; } - public static IEnumerable GetContractEntriesDao(int count) + public static IEnumerable GetContractEntriesDao(int count) { var contractList = GetContractEntries(count).Select(i => - i.ToDao(MapperProvider)); + i.ToDao(MapperProvider)); return contractList; } diff --git a/src/Catalyst.TestUtils/ProtocolHelpers/PublicEntryHelper.cs b/src/Catalyst.TestUtils/ProtocolHelpers/PublicEntryHelper.cs index 4bb9f320b5..d4b45290a2 100644 --- a/src/Catalyst.TestUtils/ProtocolHelpers/PublicEntryHelper.cs +++ b/src/Catalyst.TestUtils/ProtocolHelpers/PublicEntryHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,7 +25,9 @@ using System.Collections.Generic; using System.Linq; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Transaction; using Catalyst.Core.Lib.Extensions; +using Catalyst.Core.Lib.Util; using Catalyst.Protocol.Transaction; using Nethermind.Core.Extensions; @@ -37,12 +39,14 @@ public static class PublicEntryHelper public static PublicEntry GetPublicEntry() { - var amount = new Random().Next(78588446).ToByteArray(new Bytes.Endianness()); + var amount = new Random().Next(78588446).ToByteArray(); + var fees = new Random().Next(78588446).ToByteArray(); return new PublicEntry { Amount = amount.ToByteString(), - Base = BaseEntryHelper.GetBaseEntry() + ReceiverAddress = ByteUtil.GenerateRandomByteArray(32).ToByteString(), + SenderAddress = ByteUtil.GenerateRandomByteArray(32).ToByteString(), }; } diff --git a/src/Catalyst.TestUtils/ProtocolMessageExtensions.cs b/src/Catalyst.TestUtils/ProtocolMessageExtensions.cs index a31efc4031..209e532ac2 100644 --- a/src/Catalyst.TestUtils/ProtocolMessageExtensions.cs +++ b/src/Catalyst.TestUtils/ProtocolMessageExtensions.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -30,27 +30,28 @@ using Catalyst.Protocol.Wire; using Catalyst.TestUtils.Protocol; using Google.Protobuf; +using MultiFormats; namespace Catalyst.TestUtils { public static class ProtocolMessageExtensions { public static ProtocolMessage ToSignedProtocolMessage(this IMessage proto, - PeerId senderId, + MultiAddress sender, ISignature signature = default, SigningContext signingContext = default, ICorrelationId correlationId = default) { - return ToSignedProtocolMessage(proto, senderId, signature?.SignatureBytes, signingContext, correlationId); + return ToSignedProtocolMessage(proto, sender, signature?.SignatureBytes, signingContext, correlationId); } public static ProtocolMessage ToSignedProtocolMessage(this IMessage proto, - PeerId senderId = default, + MultiAddress sender = default, byte[] signature = default, SigningContext signingContext = default, ICorrelationId correlationId = default) { - var peerId = senderId ?? PeerIdHelper.GetPeerId("sender"); + var peerId = sender ?? MultiAddressHelper.GetAddress("sender"); var protocolMessage = proto.ToProtocolMessage(peerId, correlationId ?? CorrelationId.GenerateCorrelationId()); var newSignature = SignatureHelper.GetSignature(signature, signingContext); diff --git a/src/Catalyst.TestUtils/ProtocolMessageObserver.cs b/src/Catalyst.TestUtils/ProtocolMessageObserver.cs index 94d0f0bc45..86ed616667 100644 --- a/src/Catalyst.TestUtils/ProtocolMessageObserver.cs +++ b/src/Catalyst.TestUtils/ProtocolMessageObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,41 +24,32 @@ using System; using System.Collections.Concurrent; using System.Collections.Generic; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Core.Lib.Util; -using Catalyst.Protocol.Wire; using Serilog; namespace Catalyst.TestUtils { - public sealed class ProtocolMessageObserver : IObserver> + public sealed class ProtocolMessageObserver : IObserver { private readonly ILogger _logger; - private readonly ConcurrentStack> _received; + private readonly ConcurrentStack _received; public ProtocolMessageObserver(int index, ILogger logger) { _logger = logger; Index = index; - _received = new ConcurrentStack>(); + _received = new ConcurrentStack(); } - public IReadOnlyCollection> Received => - Array.AsReadOnly(_received.ToArray()); + public IReadOnlyCollection Received => Array.AsReadOnly(_received.ToArray()); public int Index { get; } public void OnCompleted() { _logger.Debug($"observer {Index} done"); } public void OnError(Exception error) { _logger.Debug($"observer {Index} received error : {error.Message}"); } - public void OnNext(IObserverDto value) + public void OnNext(T value) { - if (value == NullObjects.ObserverDto) - { - return; - } - - _logger.Debug($"observer {Index} received message of type {value?.Payload?.TypeUrl ?? "(null)"}"); _received.Push(value); } } diff --git a/src/Catalyst.TestUtils/Repository/EfCoreDbTestModule.cs b/src/Catalyst.TestUtils/Repository/EfCoreDbTestModule.cs index 03eda0062f..98fd7d30fa 100644 --- a/src/Catalyst.TestUtils/Repository/EfCoreDbTestModule.cs +++ b/src/Catalyst.TestUtils/Repository/EfCoreDbTestModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,13 +22,15 @@ #endregion using Autofac; -using Catalyst.Core.Lib.DAO; -using Catalyst.Core.Lib.Repository; +using Catalyst.Core.Lib.DAO.Peer; +using Catalyst.Core.Lib.DAO.Transaction; +using Catalyst.Core.Lib.Service; +using SharpRepository.EfCoreRepository; using SharpRepository.Repository; namespace Catalyst.TestUtils.Repository { - public sealed class EfCoreDbTestModule : Module + public sealed class EfCoreDbTestModule : Autofac.Module { private readonly string _connectionString; public EfCoreDbTestModule(string connectionString) { _connectionString = connectionString; } @@ -38,8 +40,8 @@ protected override void Load(ContainerBuilder builder) builder.Register(c => new EfCoreContext(_connectionString)).AsImplementedInterfaces().AsSelf() .InstancePerLifetimeScope(); - builder.RegisterType().As>().SingleInstance(); - builder.RegisterType().As>().SingleInstance(); + builder.RegisterType>().As>().SingleInstance(); + builder.RegisterType>().As>().SingleInstance(); } } } diff --git a/src/Catalyst.TestUtils/Repository/InMemoryTestModule.cs b/src/Catalyst.TestUtils/Repository/InMemoryTestModule.cs index b85752542a..4e35fc9076 100644 --- a/src/Catalyst.TestUtils/Repository/InMemoryTestModule.cs +++ b/src/Catalyst.TestUtils/Repository/InMemoryTestModule.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ namespace Catalyst.TestUtils.Repository { - public sealed class InMemoryTestModule : Module + public sealed class InMemoryTestModule : Autofac.Module where TDao : DaoBase, new() { protected override void Load(ContainerBuilder builder) diff --git a/src/Catalyst.TestUtils/Repository/MongoDbTestModule..cs b/src/Catalyst.TestUtils/Repository/MongoDbTestModule..cs index 4d9a81dcf0..589f4b3fd1 100644 --- a/src/Catalyst.TestUtils/Repository/MongoDbTestModule..cs +++ b/src/Catalyst.TestUtils/Repository/MongoDbTestModule..cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,7 +27,7 @@ namespace Catalyst.TestUtils.Repository { - public sealed class MongoDbTestModule : Module + public sealed class MongoDbTestModule : Autofac.Module where TDao : DaoBase, new() { diff --git a/src/Catalyst.TestUtils/RpcSettingsHelper.cs b/src/Catalyst.TestUtils/RpcSettingsHelper.cs index 2563dccecb..041141840f 100644 --- a/src/Catalyst.TestUtils/RpcSettingsHelper.cs +++ b/src/Catalyst.TestUtils/RpcSettingsHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -32,8 +32,7 @@ public class RpcSettingsHelper public static IRpcServerSettings GetRpcServerSettings(int port = 42051) { var settings = Substitute.For(); - settings.Port.Returns(port); - settings.BindAddress.Returns(IPAddress.Loopback); + settings.Address.Returns($"/ip4/192.168.0.181/tcp/{port}/ipfs/18n3naE9kBZoVvgYMV6saMZdwu2yu3QMzKa2BDkb5C5pcuhtrH1G9HHbztbbxA8tGmf4"); return settings; } } diff --git a/src/Catalyst.TestUtils/ScoredCandidateDeltaHelper.cs b/src/Catalyst.TestUtils/ScoredCandidateDeltaHelper.cs index ee8d59f63f..2262f5fd58 100644 --- a/src/Catalyst.TestUtils/ScoredCandidateDeltaHelper.cs +++ b/src/Catalyst.TestUtils/ScoredCandidateDeltaHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,9 +23,9 @@ using Catalyst.Abstractions.Hashing; using Catalyst.Core.Modules.Consensus.Deltas; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Wire; -using LibP2P; +using Lib.P2P; +using Nethermind.Core; namespace Catalyst.TestUtils { @@ -42,7 +42,7 @@ public static ScoredCandidateDelta GetScoredCandidateDelta(IHashProvider hashPro public static ScoredCandidateDelta GetScoredCandidateDelta(IHashProvider hashProvider, Cid previousDeltaHash = null, Cid hash = null, - PeerId producerId = null, + Address producerId = null, int score = 0) { var candidateDelta = DeltaHelper.GetCandidateDelta(hashProvider, hash: hash, producerId: producerId, diff --git a/src/Catalyst.TestUtils/SelfAwareTestBase.cs b/src/Catalyst.TestUtils/SelfAwareTestBase.cs index 3578517a79..cb58ae5a4c 100644 --- a/src/Catalyst.TestUtils/SelfAwareTestBase.cs +++ b/src/Catalyst.TestUtils/SelfAwareTestBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,9 +21,10 @@ #endregion +using NUnit.Framework; +using NUnit.Framework.Interfaces; using System; using System.Reflection; -using Xunit.Abstractions; namespace Catalyst.TestUtils { @@ -34,22 +35,18 @@ public class SelfAwareTestBase { protected ITest CurrentTest; protected string CurrentTestName; - protected ITestOutputHelper Output; + protected TestContext Output; - protected SelfAwareTestBase(ITestOutputHelper output) + protected SelfAwareTestBase() { - Output = output; - CurrentTest = Output.GetType() - .GetField("test", BindingFlags.Instance | BindingFlags.NonPublic) - .GetValue(Output) as ITest; - if (CurrentTest == null) - { - throw new ArgumentNullException( - $"Failed to reflect current test as {nameof(ITest)} from {nameof(output)}"); - } + } + + public virtual void Setup(TestContext output) + { + Output = output; - CurrentTestName = CurrentTest.TestCase.TestMethod.Method.Name; + CurrentTestName = output.Test.MethodName; } } } diff --git a/src/Catalyst.TestUtils/SocketPortHelper.cs b/src/Catalyst.TestUtils/SocketPortHelper.cs index a9d456e496..8573c1b688 100644 --- a/src/Catalyst.TestUtils/SocketPortHelper.cs +++ b/src/Catalyst.TestUtils/SocketPortHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.TestUtils/StoredItem.cs b/src/Catalyst.TestUtils/StoredItem.cs index 739f6b0d89..9be1160181 100644 --- a/src/Catalyst.TestUtils/StoredItem.cs +++ b/src/Catalyst.TestUtils/StoredItem.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.TestUtils/StringHelper.cs b/src/Catalyst.TestUtils/StringHelper.cs index e4832df904..4a57acd1f4 100644 --- a/src/Catalyst.TestUtils/StringHelper.cs +++ b/src/Catalyst.TestUtils/StringHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -28,7 +28,7 @@ namespace Catalyst.TestUtils { public static class StringHelper { - private static readonly Random Random = new Random(); + private static readonly Random Random = new(); public static string RandomString(int length = 15) { diff --git a/src/Catalyst.TestUtils/SubstituteResults.cs b/src/Catalyst.TestUtils/SubstituteResults.cs index e5a2384ba2..e379a43376 100644 --- a/src/Catalyst.TestUtils/SubstituteResults.cs +++ b/src/Catalyst.TestUtils/SubstituteResults.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -29,7 +29,7 @@ namespace Catalyst.TestUtils //nice idea from https://stackoverflow.com/questions/12163840/nsubstitute-multiple-return-sequence public class SubstituteResults { - private readonly Queue> _values = new Queue>(); + private readonly Queue> _values = new(); public SubstituteResults(T result) { _values.Enqueue(() => result); } public SubstituteResults(Func value) { _values.Enqueue(value); } public SubstituteResults Then(T value) { return Then(() => value); } diff --git a/src/Catalyst.TestUtils/TaskHelper.cs b/src/Catalyst.TestUtils/TaskHelper.cs index 9371830bd9..fc43dcf5f3 100644 --- a/src/Catalyst.TestUtils/TaskHelper.cs +++ b/src/Catalyst.TestUtils/TaskHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -27,9 +27,7 @@ using System.Threading; using System.Threading.Tasks; using FluentAssertions; -using Xunit; -using Xunit.Abstractions; -using Xunit.Sdk; +using NUnit.Framework; namespace Catalyst.TestUtils { @@ -46,7 +44,7 @@ public static async Task WaitForAsync(Func condition, TimeSpan times { if (!condition()) { - Task.Delay(100).ConfigureAwait(false).GetAwaiter().GetResult(); + Task.Delay(100, default).ConfigureAwait(false).GetAwaiter().GetResult(); } else { @@ -69,7 +67,7 @@ internal static async Task WaitForAsyncOrThrowAsync(Expression> condi var delay = waitPeriod == default ? TimeSpan.FromMilliseconds(100) : waitPeriod; var performCheck = condition.Compile(); - var stopWatch = new Stopwatch(); + Stopwatch stopWatch = new(); stopWatch.Start(); while (!performCheck()) { @@ -83,10 +81,9 @@ internal static async Task WaitForAsyncOrThrowAsync(Expression> condi public class TaskHelperTests { - private readonly ITestOutputHelper _output; - public TaskHelperTests(ITestOutputHelper output) { _output = output; } + public TaskHelperTests() { } - [Fact] + [Test] public async Task WaitFor_Should_Throw_Descriptive_Message_After_Timeout() { var attempts = 0; @@ -94,7 +91,7 @@ public async Task WaitFor_Should_Throw_Descriptive_Message_After_Timeout() var waitDelay = TimeSpan.FromMilliseconds(100); var timeout = TimeSpan.FromMilliseconds(500); - var watch = new Stopwatch(); + Stopwatch watch = new(); watch.Start(); var success = await TaskHelper.WaitForAsync( () => IncreaseAndCheckIfAboveLimit(ref attempts, @@ -109,18 +106,18 @@ public async Task WaitFor_Should_Throw_Descriptive_Message_After_Timeout() private bool IncreaseAndCheckIfAboveLimit(ref int count, int limit) { - _output.WriteLine($"attempt: {++count}"); + TestContext.Out.WriteLine($"attempt: {++count}"); return limit < count; } - [Fact] + [Test] public async Task WaitFor_Should_Return_As_Soon_As_Possible() { var attempts = 0; var timeout = TimeSpan.FromSeconds(1); - var watch = new Stopwatch(); + Stopwatch watch = new(); watch.Start(); var success = await TaskHelper.WaitForAsync( () => IncreaseAndCheckIfAboveLimit(ref attempts, 2), timeout) @@ -132,7 +129,7 @@ public async Task WaitFor_Should_Return_As_Soon_As_Possible() watch.Elapsed.Should().BeLessOrEqualTo(timeout.Multiply(2)); } - [Fact] + [Test] public void WaitForAsyncOrThrow_Should_Throw_Descriptive_Message_After_Timeout() { var attempts = 0; @@ -140,20 +137,20 @@ public void WaitForAsyncOrThrow_Should_Throw_Descriptive_Message_After_Timeout() var waitDelay = TimeSpan.FromMilliseconds(50); var timeout = TimeSpan.FromMilliseconds(500); - var watch = new Stopwatch(); + Stopwatch watch = new(); watch.Start(); new Func(async () => await TaskHelper.WaitForAsyncOrThrowAsync( () => IncreaseAndCheckIfAboveLimit(ref attempts, (int) (timeout.TotalMilliseconds / waitDelay.TotalMilliseconds) + 1), timeout, waitDelay) - .ConfigureAwait(false)).Should().Throw() - .And.Message.Should().Contain(nameof(IncreaseAndCheckIfAboveLimit)); + .ConfigureAwait(false)).Should().ThrowAsync() + .WithMessage(nameof(IncreaseAndCheckIfAboveLimit)); watch.Stop(); attempts.Should().BeGreaterOrEqualTo(9); watch.Elapsed.Should().BeGreaterOrEqualTo(timeout); } - [Fact] + [Test] public async Task WaitForAsyncOrThrow_Should_Return_As_Soon_As_Possible() { var attempts = 0; @@ -161,7 +158,7 @@ public async Task WaitForAsyncOrThrow_Should_Return_As_Soon_As_Possible() var timeout = TimeSpan.FromSeconds(1); var waitDelay = TimeSpan.FromMilliseconds(50); - var watch = new Stopwatch(); + Stopwatch watch = new(); watch.Start(); await TaskHelper.WaitForAsyncOrThrowAsync( () => IncreaseAndCheckIfAboveLimit(ref attempts, 2), timeout, waitDelay) diff --git a/src/Catalyst.TestUtils/TestCertificateStore.cs b/src/Catalyst.TestUtils/TestCertificateStore.cs index ce042c5eee..a9c7cc1b28 100644 --- a/src/Catalyst.TestUtils/TestCertificateStore.cs +++ b/src/Catalyst.TestUtils/TestCertificateStore.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.TestUtils/TestClientBase.cs b/src/Catalyst.TestUtils/TestClientBase.cs index f60750c803..5d3687f06e 100644 --- a/src/Catalyst.TestUtils/TestClientBase.cs +++ b/src/Catalyst.TestUtils/TestClientBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,18 +23,19 @@ using System; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; -using Catalyst.Core.Lib.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using NSubstitute; using Serilog; namespace Catalyst.TestUtils { - public class TestClientBase : ClientBase + public class TestClientBase : ClientBase { - public TestClientBase(IChannelFactory channelFactory, + public TestClientBase(IChannelFactory channelFactory, ILogger logger, IEventLoopGroupFactory handlerEventEventLoopGroupFactory) : base(channelFactory, logger, handlerEventEventLoopGroupFactory) diff --git a/src/Catalyst.TestUtils/TestConstants.cs b/src/Catalyst.TestUtils/TestConstants.cs index 64d35b9b76..02d406babe 100644 --- a/src/Catalyst.TestUtils/TestConstants.cs +++ b/src/Catalyst.TestUtils/TestConstants.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.TestUtils/TestCycleEventProvider.cs b/src/Catalyst.TestUtils/TestCycleEventProvider.cs index 76574407fa..4816f94d36 100644 --- a/src/Catalyst.TestUtils/TestCycleEventProvider.cs +++ b/src/Catalyst.TestUtils/TestCycleEventProvider.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -22,22 +22,23 @@ #endregion using System; +using System.Threading.Tasks; using Catalyst.Abstractions.Consensus; using Catalyst.Abstractions.Consensus.Cycle; using Catalyst.Abstractions.Consensus.Deltas; using Catalyst.Core.Modules.Consensus.Cycle; using Catalyst.Core.Modules.Hashing; using Microsoft.Reactive.Testing; +using MultiFormats.Registry; using NSubstitute; using Serilog; -using TheDotNetLeague.MultiFormats.MultiHash; namespace Catalyst.TestUtils { public class TestCycleEventProvider : ICycleEventsProvider, IDisposable { private readonly ICycleEventsProvider _cycleEventsProvider; - private readonly IDisposable _deltaUpdatesSubscription; + private IDisposable _deltaUpdatesSubscription; public TestCycleEventProvider(ILogger logger = null) { @@ -49,8 +50,8 @@ public TestCycleEventProvider(ILogger logger = null) var dateTimeProvider = Substitute.For(); var deltaHashProvider = Substitute.For(); - var hashingAlgorithm = HashingAlgorithm.GetAlgorithmMetadata("blake2b-256"); - var hashingProvider = new HashProvider(hashingAlgorithm); + var hashingAlgorithm = HashingAlgorithm.GetAlgorithmMetadata("keccak-256"); + HashProvider hashingProvider = new(hashingAlgorithm); dateTimeProvider.UtcNow.Returns(_ => Scheduler.Now.DateTime); @@ -60,9 +61,13 @@ public TestCycleEventProvider(ILogger logger = null) deltaHashProvider.GetLatestDeltaHash(Arg.Any()) .Returns(ci => hashingProvider.ComputeMultiHash( - BitConverter.GetBytes(((DateTime) ci[0]).Ticks / - (int) _cycleEventsProvider.Configuration.CycleDuration.Ticks))); + BitConverter.GetBytes(((DateTime)ci[0]).Ticks / + (int)_cycleEventsProvider.Configuration.CycleDuration.Ticks))); + } + public async Task StartAsync() + { + await _cycleEventsProvider.StartAsync(); _deltaUpdatesSubscription = PhaseChanges.Subscribe(p => CurrentPhase = p); } diff --git a/src/Catalyst.TestUtils/TestDfs.cs b/src/Catalyst.TestUtils/TestDfs.cs new file mode 100644 index 0000000000..0b325be448 --- /dev/null +++ b/src/Catalyst.TestUtils/TestDfs.cs @@ -0,0 +1,97 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Autofac; +using Catalyst.Abstractions.Cli; +using Catalyst.Abstractions.Cryptography; +using Catalyst.Abstractions.Dfs; +using Catalyst.Abstractions.Dfs.Migration; +using Catalyst.Abstractions.FileSystem; +using Catalyst.Abstractions.Hashing; +using Catalyst.Abstractions.Keystore; +using Catalyst.Abstractions.Options; +using Catalyst.Core.Lib; +using Catalyst.Core.Lib.Cli; +using Catalyst.Core.Lib.Cryptography; +using Catalyst.Core.Modules.Dfs; +using Catalyst.Core.Modules.Dfs.Migration; +using Catalyst.Core.Modules.Hashing; +using Catalyst.Core.Modules.Keystore; +using Catalyst.TestUtils; +using MultiFormats.Registry; +using Newtonsoft.Json.Linq; +using NUnit.Framework; + +namespace Catalyst.TestUtils +{ + public class TestDfs + { + private sealed class TestDfsFileSystem : FileSystemBasedTest + { + } + + public static IDfsService GetTestDfs(IFileSystem fileSystem = default, string hashName = "keccak-256", string keyType = null) + { + var nodeGuid = Guid.NewGuid(); + ContainerBuilder containerBuilder = new(); + + if (fileSystem == null) + { + TestDfsFileSystem testFileSystem = new(); + testFileSystem.Setup(TestContext.CurrentContext); + fileSystem = testFileSystem.FileSystem; + } + + containerBuilder.RegisterModule(); + containerBuilder.RegisterModule(); + containerBuilder.RegisterType().As(); + containerBuilder.RegisterInstance(new PasswordManager(new TestPasswordReader(), new PasswordRegistry())).As().SingleInstance(); + containerBuilder.RegisterInstance(fileSystem).As(); + containerBuilder.RegisterType().As(); + containerBuilder.RegisterModule(); + containerBuilder.RegisterInstance(new HashProvider(HashingAlgorithm.GetAlgorithmMetadata(hashName))).As(); + containerBuilder.RegisterType().As().SingleInstance(); + containerBuilder.RegisterModule(new DfsModule()); + containerBuilder.RegisterType().SingleInstance().WithProperty("DisableMdns", true).WithProperty("UsePeerRepository", false); + if (keyType != null) + { + containerBuilder.RegisterType().SingleInstance().WithProperty("DefaultKeyType", keyType); + } + + var container = containerBuilder.Build(); + var scope = container.BeginLifetimeScope(nodeGuid); + var dfsService = scope.Resolve(); + + dfsService.ConfigApi.SetAsync( + "Addresses.Swarm", + JToken.FromObject(new[] + { + "/ip4/0.0.0.0/tcp/0" + }) + ).Wait(); + + return dfsService; + } + } +} diff --git a/src/Catalyst.TestUtils/TestFileSystem.cs b/src/Catalyst.TestUtils/TestFileSystem.cs index 70b52597b2..010f9f6fbe 100644 --- a/src/Catalyst.TestUtils/TestFileSystem.cs +++ b/src/Catalyst.TestUtils/TestFileSystem.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -40,8 +40,8 @@ public class TestFileSystem : IFileSystem public TestFileSystem(string rootPath) { - var rootDirectory = new DirectoryInfo(rootPath); - _fileSystem = Substitute.ForPartsOf(); + DirectoryInfo rootDirectory = new(rootPath); + _fileSystem = Substitute.For(); _fileSystem.GetCatalystDataDir().Returns(rootDirectory); _retryPolicy = Policy.Handle() .WaitAndRetry(5, i => TimeSpan.FromMilliseconds(500).Multiply(i)); @@ -74,9 +74,7 @@ public async Task WriteTextFileToCddSubDirectoryAsync(string fileName } public bool DataFileExists(string fileName) { return _fileSystem.DataFileExists(fileName); } - public bool DataFileExistsInSubDirectory(string fileName, string subDirectory) { return _fileSystem.DataFileExistsInSubDirectory(fileName, subDirectory); } - public string ReadTextFromCddFile(string fileName) { return _fileSystem.ReadTextFromCddFile(fileName); } - + public string ReadTextFromCddSubDirectoryFile(string fileName, string subDirectory) { var content = _retryPolicy.Execute(() => _fileSystem.ReadTextFromCddSubDirectoryFile(fileName, subDirectory)); diff --git a/src/Catalyst.TestUtils/TestKeyRegistry.cs b/src/Catalyst.TestUtils/TestKeyRegistry.cs index 1f56b0ebb3..ea763f7031 100644 --- a/src/Catalyst.TestUtils/TestKeyRegistry.cs +++ b/src/Catalyst.TestUtils/TestKeyRegistry.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -25,6 +25,7 @@ using Catalyst.Abstractions.Types; using Catalyst.Core.Lib.Util; using Catalyst.Core.Modules.Cryptography.BulletProofs; +using MultiFormats; using NSubstitute; namespace Catalyst.TestUtils @@ -32,20 +33,21 @@ namespace Catalyst.TestUtils public static class TestKeyRegistry { public static readonly string TestPrivateKey = "ftqm5kpzpo7bvl6e53q5j6mmrjwupbbiuszpsopxvjodkkqqiusa"; - public static readonly string TestPublicKey; // = "qnb9bw3b2yj4hpjcmsvgp12bkwff313v9gaqb18atvwfpevrmmf0" + public static readonly string TestPublicKey; static TestKeyRegistry() { - var cryptoContext = new FfiWrapper(); - var fakePrivateKey = cryptoContext.GetPrivateKeyFromBytes(TestPrivateKey.KeyToBytes()); + FfiWrapper cryptoContext = new(); + var fakePrivateKey = cryptoContext.GetPrivateKeyFromBytes(TestPrivateKey.FromBase32()); TestPublicKey = fakePrivateKey.GetPublicKey().Bytes.KeyToString(); } public static IKeyRegistry MockKeyRegistry() { var keyRegistry = Substitute.For(); - var cryptoContext = new FfiWrapper(); - var fakePrivateKey = cryptoContext.GetPrivateKeyFromBytes(TestPrivateKey.KeyToBytes()); + FfiWrapper cryptoContext = new(); + var fakePrivateKey = cryptoContext.GetPrivateKeyFromBytes(TestPrivateKey.FromBase32()); + keyRegistry.RegistryContainsKey(Arg.Any()).Returns(true); keyRegistry.GetItemFromRegistry(KeyRegistryTypes.DefaultKey).Returns(fakePrivateKey); keyRegistry.Contains(Arg.Any()).Returns(true); return keyRegistry; diff --git a/src/Catalyst.TestUtils/TestMappers.cs b/src/Catalyst.TestUtils/TestMappers.cs index 9861bdc690..d6ed74ef91 100644 --- a/src/Catalyst.TestUtils/TestMappers.cs +++ b/src/Catalyst.TestUtils/TestMappers.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,13 @@ using Catalyst.Abstractions.DAO; using Catalyst.Core.Lib.DAO; +using Catalyst.Core.Lib.DAO.Cryptography; using Catalyst.Core.Lib.DAO.Deltas; +using Catalyst.Core.Lib.DAO.Ledger; +using Catalyst.Core.Lib.DAO.Peer; +using Catalyst.Core.Lib.DAO.Transaction; +using Catalyst.Core.Modules.Hashing; +using MultiFormats.Registry; namespace Catalyst.TestUtils { @@ -42,12 +48,11 @@ public TestMapperProvider() : base(new IMapperInitializer[] new DeltaDfsHashBroadcastMapperInitialiser(), new FavouriteDeltaBroadcastMapperInitialiser(), new CoinbaseEntryMapperInitialiser(), - new PublicEntryMapperInitialiser(), + new PublicEntryMapperInitialiser(new HashProvider(HashingAlgorithm.GetAlgorithmMetadata("keccak-256"))), new ConfidentialEntryMapperInitialiser(), new TransactionBroadcastMapperInitialiser(), - new ContractEntryMapperInitialiser(), new SignatureMapperInitialiser(), - new BaseEntryMapperInitialiser(), + new DeltaIndexMapperInitialiser() }) { } } } diff --git a/src/Catalyst.TestUtils/TestMempoolDocumentRepository.cs b/src/Catalyst.TestUtils/TestMempoolDocumentRepository.cs deleted file mode 100644 index 33d927d404..0000000000 --- a/src/Catalyst.TestUtils/TestMempoolDocumentRepository.cs +++ /dev/null @@ -1,74 +0,0 @@ -#region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see . -*/ - -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using Catalyst.Abstractions.Mempool.Repositories; -using Catalyst.Core.Lib.DAO; -using Catalyst.Core.Lib.Repository; -using Catalyst.Protocol.Wire; -using SharpRepository.Repository; - -namespace Catalyst.TestUtils -{ - public sealed class TestMempoolRepository : RepositoryWrapper, - IMempoolRepository - { - private readonly TestMapperProvider _mapperProvider; - - public TestMempoolRepository(IRepository repository) : base(repository) - { - _mapperProvider = new TestMapperProvider(); - } - - public bool TryReadItem(string key) - { - throw new NotImplementedException(); - } - - public TransactionBroadcastDao ReadItem(string key) - { - throw new NotImplementedException(); - } - - public bool DeleteItem(params string[] transactionSignatures) - { - throw new NotImplementedException(); - } - - public bool CreateItem(TransactionBroadcastDao transactionBroadcast) - { - throw new NotImplementedException(); - } - - public new IEnumerable GetAll() - { - var utcNow = DateTime.UtcNow; - var tenSecondSlot = 1 + utcNow.Second / 10; - var tx = TransactionHelper.GetPublicTransaction(timestamp: (long) utcNow.ToOADate()); - return Enumerable.Repeat(tx, tenSecondSlot) - .Select(x => x.ToDao(_mapperProvider)); - } - } -} diff --git a/src/Catalyst.TestUtils/TestMessageObserver.cs b/src/Catalyst.TestUtils/TestMessageObserver.cs index 884edba1b9..0028a1baa4 100644 --- a/src/Catalyst.TestUtils/TestMessageObserver.cs +++ b/src/Catalyst.TestUtils/TestMessageObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,47 +23,47 @@ using System; using Catalyst.Abstractions.IO.Messaging.Correlation; -using Catalyst.Abstractions.IO.Messaging.Dto; using Catalyst.Abstractions.IO.Observers; using Catalyst.Core.Lib.Extensions; using Catalyst.Core.Lib.IO.Observers; -using Catalyst.Protocol.Peer; +using Catalyst.Modules.Network.Dotnetty.IO.Observers; using Catalyst.Protocol.Wire; -using DotNetty.Transport.Channels; using Google.Protobuf; +using MultiFormats; using NSubstitute; using Serilog; namespace Catalyst.TestUtils { - public class TestMessageObserver : MessageObserverBase, - IP2PMessageObserver, IRpcResponseObserver, IRpcRequestObserver + public class TestMessageObserver : MessageObserverBase, + IP2PMessageObserver where TProto : IMessage, IMessage { + private static Func FilterExpression = m => m?.TypeUrl != null && m.TypeUrl == typeof(TProto).ShortenedProtoFullName(); + public IObserver SubstituteObserver { get; } - public PeerId PeerId { get; } + public MultiAddress Address { get; } - public TestMessageObserver(ILogger logger) : base(logger, typeof(TProto).ShortenedProtoFullName()) + public TestMessageObserver(ILogger logger) : base(logger, FilterExpression) { SubstituteObserver = Substitute.For>(); - PeerId = PeerIdHelper.GetPeerId(); + Address = MultiAddressHelper.GetAddress(); } public override void OnError(Exception exception) { SubstituteObserver.OnError(exception); } - public void HandleResponse(IObserverDto messageDto) + public void HandleResponse(ProtocolMessage message) { - SubstituteObserver.OnNext(messageDto.Payload.FromProtocolMessage()); + SubstituteObserver.OnNext(message.FromProtocolMessage()); } - public override void OnNext(IObserverDto messageDto) + public override void OnNext(ProtocolMessage message) { - SubstituteObserver.OnNext(messageDto.Payload.FromProtocolMessage()); + SubstituteObserver.OnNext(message.FromProtocolMessage()); } - public void HandleResponseObserver(IMessage messageDto, - IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + public void HandleResponseObserver(IMessage message, + MultiAddress address, ICorrelationId correlationId) { throw new NotImplementedException(); diff --git a/src/Catalyst.TestUtils/TestPasswordReader.cs b/src/Catalyst.TestUtils/TestPasswordReader.cs index f2993146a5..2a9bc70618 100644 --- a/src/Catalyst.TestUtils/TestPasswordReader.cs +++ b/src/Catalyst.TestUtils/TestPasswordReader.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -38,7 +38,7 @@ public TestPasswordReader(string expectedPassword = "password") public static SecureString BuildSecureStringPassword(string expectedPassword) { - var secureString = new SecureString(); + SecureString secureString = new(); expectedPassword.ToList().ForEach(c => secureString.AppendChar(c)); secureString.MakeReadOnly(); return secureString; diff --git a/src/Catalyst.TestUtils/TestRpcResponseObserver.cs b/src/Catalyst.TestUtils/TestRpcResponseObserver.cs index c1f3aa9dae..e6f8aaccff 100644 --- a/src/Catalyst.TestUtils/TestRpcResponseObserver.cs +++ b/src/Catalyst.TestUtils/TestRpcResponseObserver.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -24,9 +24,9 @@ using System; using Catalyst.Abstractions.IO.Messaging.Correlation; using Catalyst.Core.Lib.Rpc.IO; -using Catalyst.Protocol.Peer; using Catalyst.Protocol.Rpc.Node; using DotNetty.Transport.Channels; +using MultiFormats; using Serilog; namespace Catalyst.TestUtils @@ -44,11 +44,11 @@ public TestRpcResponseObserver(ILogger logger) /// /// /// - /// + /// /// protected override void HandleResponse(VersionResponse testResponse, IChannelHandlerContext channelHandlerContext, - PeerId senderPeerIdentifier, + MultiAddress address, ICorrelationId correlationId) { /* Empty method as this is a Stub class for testing base */ diff --git a/src/Catalyst.TestUtils/TestSocketBase.cs b/src/Catalyst.TestUtils/TestSocketBase.cs index dcc9d6bc5b..48b0bde8f7 100644 --- a/src/Catalyst.TestUtils/TestSocketBase.cs +++ b/src/Catalyst.TestUtils/TestSocketBase.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,18 +23,19 @@ using System; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; -using Catalyst.Core.Lib.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using NSubstitute; using Serilog; namespace Catalyst.TestUtils { - public sealed class TestSocketBase : SocketBase + public sealed class TestSocketBase : SocketBase { - public TestSocketBase(IChannelFactory channelFactory, + public TestSocketBase(IChannelFactory channelFactory, ILogger logger, IEventLoopGroupFactory eventLoopGroupFactory) : base(channelFactory, logger, eventLoopGroupFactory) { diff --git a/src/Catalyst.TestUtils/TestTcpClient.cs b/src/Catalyst.TestUtils/TestTcpClient.cs index 664df4df38..8dfbba06ba 100644 --- a/src/Catalyst.TestUtils/TestTcpClient.cs +++ b/src/Catalyst.TestUtils/TestTcpClient.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,18 +23,19 @@ using System; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; -using Catalyst.Core.Lib.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using NSubstitute; using Serilog; namespace Catalyst.TestUtils { - public class TestTcpClient : TcpClient + public class TestTcpClient : TcpClient { - public TestTcpClient(ITcpClientChannelFactory tcpClientChannelFactory, + public TestTcpClient(ITcpClientChannelFactory tcpClientChannelFactory, ILogger logger, ITcpClientEventLoopGroupFactory eventLoopGroupFactory) : base(tcpClientChannelFactory, logger, eventLoopGroupFactory) { diff --git a/src/Catalyst.TestUtils/TestTcpClientChannelFactory.cs b/src/Catalyst.TestUtils/TestTcpClientChannelFactory.cs index 3a20f40a6a..e2261fb455 100644 --- a/src/Catalyst.TestUtils/TestTcpClientChannelFactory.cs +++ b/src/Catalyst.TestUtils/TestTcpClientChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,22 +23,23 @@ using System; using System.Collections.Generic; -using System.Net; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; using Catalyst.Abstractions.IO.Handlers; -using Catalyst.Abstractions.IO.Transport.Channels; -using Catalyst.Core.Lib.IO.Handlers; -using Catalyst.Core.Lib.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; +using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; +using MultiFormats; using NSubstitute; namespace Catalyst.TestUtils { - public class TestTcpClientChannelFactory : TcpClientChannelFactory + public class TestTcpClientChannelFactory : TcpClientChannelFactory { - private readonly IObservableServiceHandler _observableServiceHandler; + private readonly IObservableServiceHandler _observableServiceHandler; public TestTcpClientChannelFactory(int backLogValue = 100) : base(backLogValue) { @@ -60,16 +61,15 @@ protected override Func> HandlerGenerationFunction /// Ignored /// Ignored /// Local TLS certificate - public override async Task BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + public override async Task> BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, + MultiAddress multiAddress, X509Certificate2 certificate = null) { var channel = Substitute.For(); var messageStream = _observableServiceHandler.MessageStream; - return await Task.FromResult(new ObservableChannel(messageStream, channel)).ConfigureAwait(false); + return await Task.FromResult(new ObservableChannel(messageStream, channel)).ConfigureAwait(false); } } } diff --git a/src/Catalyst.TestUtils/TestTcpServer.cs b/src/Catalyst.TestUtils/TestTcpServer.cs index 86b81a694f..e8d405eb0a 100644 --- a/src/Catalyst.TestUtils/TestTcpServer.cs +++ b/src/Catalyst.TestUtils/TestTcpServer.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,18 +23,19 @@ using System; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; -using Catalyst.Abstractions.IO.Transport.Channels; -using Catalyst.Core.Lib.IO.Transport; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Transport; +using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; using NSubstitute; using Serilog; namespace Catalyst.TestUtils { - public class TestTcpServer : TcpServer + public class TestTcpServer : TcpServer { - public TestTcpServer(ITcpServerChannelFactory tcpServerChannelFactory, + public TestTcpServer(ITcpServerChannelFactory tcpServerChannelFactory, ILogger logger, IEventLoopGroupFactory eventLoopGroupFactory) : base(tcpServerChannelFactory, logger, eventLoopGroupFactory) { diff --git a/src/Catalyst.TestUtils/TestTcpServerChannelFactory.cs b/src/Catalyst.TestUtils/TestTcpServerChannelFactory.cs index 3742301ea2..61104dacc1 100644 --- a/src/Catalyst.TestUtils/TestTcpServerChannelFactory.cs +++ b/src/Catalyst.TestUtils/TestTcpServerChannelFactory.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,22 +23,23 @@ using System; using System.Collections.Generic; -using System.Net; using System.Security.Cryptography.X509Certificates; using System.Threading.Tasks; -using Catalyst.Abstractions.IO.EventLoop; using Catalyst.Abstractions.IO.Handlers; -using Catalyst.Abstractions.IO.Transport.Channels; -using Catalyst.Core.Lib.IO.Handlers; -using Catalyst.Core.Lib.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.EventLoop; +using Catalyst.Modules.Network.Dotnetty.Abstractions.IO.Transport.Channels; +using Catalyst.Modules.Network.Dotnetty.IO.Handlers; +using Catalyst.Modules.Network.Dotnetty.IO.Transport.Channels; +using Catalyst.Protocol.Wire; using DotNetty.Transport.Channels; +using MultiFormats; using NSubstitute; namespace Catalyst.TestUtils { - public class TestTcpServerChannelFactory : TcpServerChannelFactory + public class TestTcpServerChannelFactory : TcpServerChannelFactory { - private readonly IObservableServiceHandler _observableServiceHandler; + private readonly IObservableServiceHandler _observableServiceHandler; public TestTcpServerChannelFactory(int backLogValue = 100) : base(backLogValue) { @@ -60,24 +61,22 @@ protected override Func> HandlerGenerationFunction /// Ignored /// Ignored /// Local TLS certificate - public override async Task BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + public override async Task> BuildChannelAsync(IEventLoopGroupFactory eventLoopGroupFactory, + MultiAddress address, X509Certificate2 certificate = null) { var channel = Substitute.For(); var messageStream = _observableServiceHandler.MessageStream; - return await Task.FromResult(new ObservableChannel(messageStream, channel)).ConfigureAwait(false); + return await Task.FromResult(new ObservableChannel(messageStream, channel)).ConfigureAwait(false); } public new Task BootstrapAsync(IEventLoopGroupFactory handlerEventLoopGroupFactory, - IPAddress targetAddress, - int targetPort, + MultiAddress address, X509Certificate2 certificate) { - return base.BootstrapAsync(handlerEventLoopGroupFactory, targetAddress, targetPort, certificate); + return base.BootstrapAsync(handlerEventLoopGroupFactory, address, certificate); } } } diff --git a/src/Catalyst.TestUtils/Traits.cs b/src/Catalyst.TestUtils/Traits.cs index 07f2b54852..6f7f93299f 100644 --- a/src/Catalyst.TestUtils/Traits.cs +++ b/src/Catalyst.TestUtils/Traits.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * diff --git a/src/Catalyst.TestUtils/TransactionHelper.cs b/src/Catalyst.TestUtils/TransactionHelper.cs index 693013aa28..47a8bc5611 100644 --- a/src/Catalyst.TestUtils/TransactionHelper.cs +++ b/src/Catalyst.TestUtils/TransactionHelper.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -41,72 +41,58 @@ public static TransactionBroadcast GetPublicTransaction(uint amount = 123, long timestamp = 12345, ulong transactionFees = 2, ulong nonce = 0, + ulong gasPrice = 1, NetworkType networkType = NetworkType.Devnet) { var transaction = new TransactionBroadcast { - PublicEntries = + PublicEntry = new PublicEntry { - new PublicEntry + Amount = ((UInt256) amount).ToUint256ByteString(), + Nonce = nonce, + ReceiverAddress = receiverPublicKey.ToUtf8ByteString(), + SenderAddress = senderPublicKey.ToUtf8ByteString(), + GasPrice = ((UInt256) gasPrice).ToUint256ByteString(), + Signature = new Signature { - Amount = ((UInt256) amount).ToUint256ByteString(), - Base = new BaseEntry - { - Nonce = nonce, - ReceiverPublicKey = receiverPublicKey.ToUtf8ByteString(), - SenderPublicKey = senderPublicKey.ToUtf8ByteString(), - TransactionFees = ((UInt256) transactionFees).ToUint256ByteString(), - } + SigningContext = new SigningContext + {NetworkType = networkType, SignatureType = SignatureType.TransactionPublic}, + RawBytes = signature.ToUtf8ByteString() } - }, - Timestamp = new Timestamp {Seconds = timestamp}, - Signature = new Signature - { - SigningContext = new SigningContext {NetworkType = networkType, SignatureType = SignatureType.TransactionPublic}, - RawBytes = signature.ToUtf8ByteString() } }; + return transaction; } - + public static TransactionBroadcast GetContractTransaction(ByteString data, UInt256 amount, uint gasLimit, UInt256 gasPrice, - byte[] targetContract = null, // to be reviewed - string senderPublicKey = "sender", - string receiverPublicKey = "receiver", + string senderPublicKey = null, + string receiverPublicKey = null, string signature = "signature", long timestamp = 12345, - ulong transactionFees = 2, ulong nonce = 0, NetworkType networkType = NetworkType.Devnet) { var transaction = new TransactionBroadcast { - ContractEntries = + PublicEntry = new PublicEntry { - new ContractEntry + Amount = amount.ToUint256ByteString(), + Nonce = nonce, + ReceiverAddress = receiverPublicKey?.ToUtf8ByteString() ?? ByteString.CopyFrom(new byte[20]), + SenderAddress = senderPublicKey?.ToUtf8ByteString() ?? ByteString.CopyFrom(new byte[20]), + Signature = new Signature { - Amount = amount.ToUint256ByteString(), - Base = new BaseEntry - { - Nonce = nonce, - ReceiverPublicKey = receiverPublicKey.ToUtf8ByteString(), - SenderPublicKey = senderPublicKey.ToUtf8ByteString(), - TransactionFees = ((UInt256) transactionFees).ToUint256ByteString(), - }, - Data = data, - GasLimit = gasLimit, - GasPrice = gasPrice, - TargetContract = targetContract - } - }, - Timestamp = new Timestamp {Seconds = timestamp}, - Signature = new Signature - { - SigningContext = new SigningContext {NetworkType = networkType, SignatureType = SignatureType.TransactionPublic}, - RawBytes = signature.ToUtf8ByteString() + SigningContext = new SigningContext + {NetworkType = networkType, SignatureType = SignatureType.TransactionPublic}, + RawBytes = signature.ToUtf8ByteString() + }, + Data = data, + GasLimit = gasLimit, + GasPrice = gasPrice.ToUint256ByteString(), } }; diff --git a/src/Catalyst.Tools.KeyGenerator/Catalyst.Tools.KeyGenerator.csproj b/src/Catalyst.Tools.KeyGenerator/Catalyst.Tools.KeyGenerator.csproj index 18c71be438..b8b872c088 100644 --- a/src/Catalyst.Tools.KeyGenerator/Catalyst.Tools.KeyGenerator.csproj +++ b/src/Catalyst.Tools.KeyGenerator/Catalyst.Tools.KeyGenerator.csproj @@ -1,19 +1,22 @@ - - Exe - netcoreapp3.0 - + + Exe + net6.0 + + + 1701;1702;CS8002 + + + + + + + + - - - - - - - - - - + + + diff --git a/src/Catalyst.Tools.KeyGenerator/Commands/GenerateKeyStore.cs b/src/Catalyst.Tools.KeyGenerator/Commands/GenerateKeyStore.cs index 3c583e7a12..0e4d0e4eab 100644 --- a/src/Catalyst.Tools.KeyGenerator/Commands/GenerateKeyStore.cs +++ b/src/Catalyst.Tools.KeyGenerator/Commands/GenerateKeyStore.cs @@ -57,39 +57,39 @@ public bool Parse(string[] args) public void ParseOption(NetworkType networkType, object option) { - var generateKeyStoreOption = (GenerateKeyStoreOption) option; + //var generateKeyStoreOption = (GenerateKeyStoreOption) option; - if (!_fileSystem.SetCurrentPath(generateKeyStoreOption.Path)) - { - _userOutput.WriteLine("Invalid path."); - return; - } + //if (!_fileSystem.SetCurrentPath(generateKeyStoreOption.Path)) + //{ + // _userOutput.WriteLine("Invalid path."); + // return; + //} - var secureStr = _passwordLoader.PreloadPassword(generateKeyStoreOption.Password); - Exception error = null; + //var secureStr = _passwordLoader.PreloadPassword(generateKeyStoreOption.Password); + //Exception error = null; - try - { - var privateKey = _keyStore.KeyStoreGenerateAsync(networkType, KeyRegistryTypes.DefaultKey).ConfigureAwait(false) - .GetAwaiter().GetResult(); - var publicKey = privateKey.GetPublicKey().Bytes.KeyToString(); + //try + //{ + // var privateKey = _keyStore.KeyStoreGenerateAsync(networkType, KeyRegistryTypes.DefaultKey).ConfigureAwait(false) + // .GetAwaiter().GetResult(); + // var publicKey = privateKey.GetPublicKey().Bytes.KeyToString(); - _userOutput.WriteLine($"Generated key store at path: {Path.GetFullPath(generateKeyStoreOption.Path)}"); - _userOutput.WriteLine($"Public Key: {publicKey}"); - } - catch (Exception e) - { - _userOutput.WriteLine($"Error generating keystore at path {generateKeyStoreOption.Path}"); - error = e; - } - finally - { - secureStr?.Dispose(); - if (error != null) - { - throw error; - } - } + // _userOutput.WriteLine($"Generated key store at path: {Path.GetFullPath(generateKeyStoreOption.Path)}"); + // _userOutput.WriteLine($"Public Key: {publicKey}"); + //} + //catch (Exception e) + //{ + // _userOutput.WriteLine($"Error generating keystore at path {generateKeyStoreOption.Path}"); + // error = e; + //} + //finally + //{ + // secureStr?.Dispose(); + // if (error != null) + // { + // throw error; + // } + //} } public string CommandName => "generate"; diff --git a/src/Catalyst.Tools.KeyGenerator/Interfaces/IKeyGeneratorCommand.cs b/src/Catalyst.Tools.KeyGenerator/Interfaces/IKeyGeneratorCommand.cs index b2f39eb8a4..6e7d3131e2 100644 --- a/src/Catalyst.Tools.KeyGenerator/Interfaces/IKeyGeneratorCommand.cs +++ b/src/Catalyst.Tools.KeyGenerator/Interfaces/IKeyGeneratorCommand.cs @@ -21,7 +21,7 @@ #endregion -using Catalyst.Abstractions.Cli.CommandTypes; +using Catalyst.Modules.Network.Dotnetty.Abstractions.Cli.CommandTypes; using Catalyst.Protocol.Network; namespace Catalyst.Tools.KeyGenerator.Interfaces diff --git a/src/Catalyst.sln b/src/Catalyst.sln index ea360f7424..192e3dfba9 100644 --- a/src/Catalyst.sln +++ b/src/Catalyst.sln @@ -1,12 +1,10 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.28902.138 +# Visual Studio Version 17 +VisualStudioVersion = 17.0.32014.148 MinimumVisualStudioVersion = 15.0.26124.0 Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.TestUtils", "Catalyst.TestUtils\Catalyst.TestUtils.csproj", "{B276137D-FB34-48DB-80CF-ADA34A2D2F96}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Simulator", "Catalyst.Simulator\Catalyst.Simulator.csproj", "{EC74EF99-CF2A-4F19-9BE1-0D0639F76C36}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Abstractions", "Catalyst.Abstractions\Catalyst.Abstractions.csproj", "{91A5F73C-5FE6-4BF3-B437-10FA9DF785E9}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "nethermind", "nethermind", "{6074E807-FBBC-41D0-9205-C7B79513D683}" @@ -21,8 +19,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.HashLib", "..\su EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Logging", "..\submodules\nethermind\src\Nethermind\Nethermind.Logging\Nethermind.Logging.csproj", "{A3386257-A47B-407B-AB5A-A2CBAF2CEDF9}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Store", "..\submodules\nethermind\src\Nethermind\Nethermind.Store\Nethermind.Store.csproj", "{F5BA5ECB-3C48-4379-9204-5BA30705D801}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Kvm", "Catalyst.Core.Modules.Kvm\Catalyst.Core.Modules.Kvm.csproj", "{6A2D2F12-3E0F-4784-A688-56DBB50AB955}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Kvm.Tests", "Catalyst.Core.Modules.Kvm.Tests\Catalyst.Core.Modules.Kvm.Tests.csproj", "{6A7861F0-91CF-4BF5-B764-274E69443A11}" @@ -69,14 +65,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.P2P.D EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests", "Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests\Catalyst.Core.Modules.P2P.Discovery.Hastings.Tests.csproj", "{7594E59C-115B-40E9-806A-F0FFF42B2894}" EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Server", "Catalyst.Core.Modules.Rpc.Server\Catalyst.Core.Modules.Rpc.Server.csproj", "{4DE47B8A-A0C3-42C4-B00B-FFC3293D169F}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Server.Tests", "Catalyst.Core.Modules.Rpc.Server.Tests\Catalyst.Core.Modules.Rpc.Server.Tests.csproj", "{D46348E9-850A-4654-AD62-D82E560A0CEB}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Client", "Catalyst.Core.Modules.Rpc.Client\Catalyst.Core.Modules.Rpc.Client.csproj", "{C8414D13-5083-4EC8-92FC-4790C9966C7B}" -EndProject -Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Client.Tests", "Catalyst.Core.Modules.Rpc.Client.Tests\Catalyst.Core.Modules.Rpc.Client.Tests.csproj", "{93BE9DC8-E3E0-40CB-BD32-8A1170B88988}" -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Authentication", "Catalyst.Core.Modules.Authentication\Catalyst.Core.Modules.Authentication.csproj", "{67AB76B4-4AA1-4259-95CA-CC0A6735D976}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Authentication.Tests", "Catalyst.Core.Modules.Authentication.Tests\Catalyst.Core.Modules.Authentication.Tests.csproj", "{4E9C90D9-081D-4992-8C21-E93C44EB427C}" @@ -85,6 +73,7 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.Repository EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{45EAC687-ABC9-496B-B507-68AEDD4B91D5}" ProjectSection(SolutionItems) = preProject + .editorconfig = .editorconfig Common.Packable.props = Common.Packable.props Common.Projects.props = Common.Projects.props Common.TestProjects.props = Common.TestProjects.props @@ -93,19 +82,16 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Props", "Props", "{45EAC687 EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Ci", "Ci", "{29C2F736-A644-427E-B72C-FD1EE1024E27}" ProjectSection(SolutionItems) = preProject + ..\docfx-azure-pipeline.yml = ..\docfx-azure-pipeline.yml ..\mssql-e2e-azure-pipelines.yml = ..\mssql-e2e-azure-pipelines.yml ..\nuget-pre-release-azure-pipelines.yml = ..\nuget-pre-release-azure-pipelines.yml - ..\pr-test-suite-azure-pipelines.yml = ..\pr-test-suite-azure-pipelines.yml - ..\docfx-azure-pipeline.yml = ..\docfx-azure-pipeline.yml ..\poa-azure-pipeline.yml = ..\poa-azure-pipeline.yml + ..\pr-test-suite-azure-pipelines.yml = ..\pr-test-suite-azure-pipelines.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Build", "Build", "{AC999877-034E-4711-AB8D-716B1CC2C4B9}" ProjectSection(SolutionItems) = preProject ..\scripts\addLicenseHeader.sh = ..\scripts\addLicenseHeader.sh - ..\devnet-docker-compose.yml = ..\devnet-docker-compose.yml - ..\Dockerfile = ..\Dockerfile - ..\Vagrantfile = ..\Vagrantfile EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Docs", "Docs", "{8FEB1C8E-EA58-451D-A8FE-2A5189D46A0E}" @@ -122,38 +108,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Crypt EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Cryptography.BulletProofs.Tests", "Catalyst.Core.Modules.Cryptography.BulletProofs.Tests\Catalyst.Core.Modules.Cryptography.BulletProofs.Tests.csproj", "{410FCEA3-1BEC-4BF3-A12C-34EB13FAF34B}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Cryptography.FFI.Rust", "Cryptography.FFI.Rust", "{3339A04C-D933-4712-A556-F8F1B74B9753}" - ProjectSection(SolutionItems) = preProject - ..\submodules\Cryptography.FFI.Rust\azure-pipelines.yml = ..\submodules\Cryptography.FFI.Rust\azure-pipelines.yml - ..\submodules\Cryptography.FFI.Rust\Cargo.lock = ..\submodules\Cryptography.FFI.Rust\Cargo.lock - ..\submodules\Cryptography.FFI.Rust\Cargo.toml = ..\submodules\Cryptography.FFI.Rust\Cargo.toml - ..\submodules\Cryptography.FFI.Rust\LICENSE = ..\submodules\Cryptography.FFI.Rust\LICENSE - ..\submodules\Cryptography.FFI.Rust\README.md = ..\submodules\Cryptography.FFI.Rust\README.md - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{AA88D78E-377B-4452-864A-C1D5DFB06854}" - ProjectSection(SolutionItems) = preProject - ..\submodules\Cryptography.FFI.Rust\src\constants.rs = ..\submodules\Cryptography.FFI.Rust\src\constants.rs - ..\submodules\Cryptography.FFI.Rust\src\errors.rs = ..\submodules\Cryptography.FFI.Rust\src\errors.rs - ..\submodules\Cryptography.FFI.Rust\src\ffi.rs = ..\submodules\Cryptography.FFI.Rust\src\ffi.rs - ..\submodules\Cryptography.FFI.Rust\src\helpers.rs = ..\submodules\Cryptography.FFI.Rust\src\helpers.rs - ..\submodules\Cryptography.FFI.Rust\src\keys.rs = ..\submodules\Cryptography.FFI.Rust\src\keys.rs - ..\submodules\Cryptography.FFI.Rust\src\lib.rs = ..\submodules\Cryptography.FFI.Rust\src\lib.rs - ..\submodules\Cryptography.FFI.Rust\src\std_signature.rs = ..\submodules\Cryptography.FFI.Rust\src\std_signature.rs - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{46B22170-3D54-43F4-9474-C9F02DEF3A14}" - ProjectSection(SolutionItems) = preProject - ..\submodules\Cryptography.FFI.Rust\tests\integration_test.rs = ..\submodules\Cryptography.FFI.Rust\tests\integration_test.rs - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Dirichlet.Numerics", "..\submodules\nethermind\src\Dirichlet\Nethermind.Dirichlet.Numerics\Nethermind.Dirichlet.Numerics.csproj", "{70AF0C8D-A405-48A1-9914-7B6AAC66F1E2}" EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{BAB02934-9818-4691-91AF-8B56980C82C7}" - ProjectSection(SolutionItems) = preProject - ..\submodules\Cryptography.FFI.Rust\build\job-compile-native.yml = ..\submodules\Cryptography.FFI.Rust\build\job-compile-native.yml - EndProjectSection -EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.POA.Consensus", "Catalyst.Modules.POA.Consensus\Catalyst.Modules.POA.Consensus.csproj", "{BCF86A5A-8BF0-43B1-9D0C-E36108BD0F8A}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests", "Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests\Catalyst.Modules.POA.P2P.Discovery.Consortium.Tests.csproj", "{1D5BB576-6C99-4071-89EB-09BADCE09912}" @@ -166,19 +122,83 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Hashi EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Hashing.Tests", "Catalyst.Core.Modules.Hashing.Tests\Catalyst.Core.Modules.Hashing.Tests.csproj", "{2FF077F1-E652-4D17-83D2-C766E385BA5B}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Catalyst.Documentation", "..\docs\Catalyst.Documentation.csproj", "{9B99940D-EBFA-40CC-9522-6776C687EAB3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Documentation", "..\docs\Catalyst.Documentation.csproj", "{9B99940D-EBFA-40CC-9522-6776C687EAB3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Benchmark", "Catalyst.Benchmark\Catalyst.Benchmark.csproj", "{EC7C0AAF-8EC0-491C-B98E-0EC9EBBA21CE}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Node.POA.CE", "Catalyst.Node.POA.CE\Catalyst.Node.POA.CE.csproj", "{3750275D-F73D-4078-80C4-734BBD830A4E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Node.POA.CE.Tests", "Catalyst.Node.POA.CE.Tests\Catalyst.Node.POA.CE.Tests.csproj", "{36F415B5-4101-476A-9BD2-F18A5879F388}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.Server.Blazor", "Catalyst.Modules.Server.Blazor\Catalyst.Modules.Server.Blazor.csproj", "{A2B05AD3-06DA-46E6-8662-DD9757F547B4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lib.P2P", "Lib.P2P\Lib.P2P.csproj", "{767E3E1E-43AF-4A7D-8638-84E81EB13F6E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.KBucket", "Catalyst.KBucket\Catalyst.KBucket.csproj", "{F81AF4DE-30BC-48F3-B396-27087997B700}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.KBucket.Tests", "Catalyst.KBucket.Tests\Catalyst.KBucket.Tests.csproj", "{D6C18FBF-8D79-4817-940C-BF066E4E363A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Lib.P2P.Tests", "Lib.P2P.Tests\Lib.P2P.Tests.csproj", "{5BABC322-0C56-4477-978A-8EF49D7901B3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultiFormats", "MultiFormats\MultiFormats.csproj", "{76AF1E5A-94AC-44C5-B968-F816CB3A9B2E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MultiFormats.Tests", "MultiFormats.Tests\MultiFormats.Tests.csproj", "{F6142130-599A-452E-92E9-348D0560E3E7}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Serialization.Rlp", "..\submodules\nethermind\src\Nethermind\Nethermind.Serialization.Rlp\Nethermind.Serialization.Rlp.csproj", "{43B92BEC-BE53-45D7-A442-77EB14122ED5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.State", "..\submodules\nethermind\src\Nethermind\Nethermind.State\Nethermind.State.csproj", "{7DC55A67-EB89-4F6A-99D2-3E2F40D70738}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Trie", "..\submodules\nethermind\src\Nethermind\Nethermind.Trie\Nethermind.Trie.csproj", "{6646C9CA-1E73-4738-A298-7672677B5209}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Db", "..\submodules\nethermind\src\Nethermind\Nethermind.Db\Nethermind.Db.csproj", "{4DB10FC6-57AD-44AF-9623-4B1521EF5607}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Specs", "..\submodules\nethermind\src\Nethermind\Nethermind.Specs\Nethermind.Specs.csproj", "{6BEAE603-2C31-4D0D-8A69-434392BE6A1D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Config", "..\submodules\nethermind\src\Nethermind\Nethermind.Config\Nethermind.Config.csproj", "{1B71B1A0-F7BC-44C5-970E-3205AA615853}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Serialization.Json", "..\submodules\nethermind\src\Nethermind\Nethermind.Serialization.Json\Nethermind.Serialization.Json.csproj", "{65C36E26-6674-4457-A8FB-0F33EDA76759}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Crypto", "..\submodules\nethermind\src\Nethermind\Nethermind.Crypto\Nethermind.Crypto.csproj", "{DE28EED6-7281-4827-92C9-16B511BC617D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RocksDbSharp", "..\submodules\nethermind\src\rocksdb-sharp\RocksDbSharp\RocksDbSharp.csproj", "{36528129-62DF-4A4E-9337-85D51DA94EA4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Sync", "Catalyst.Core.Modules.Sync\Catalyst.Core.Modules.Sync.csproj", "{794B3026-4FEE-4AFC-AE05-7827BEA66E56}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Sync.Tests", "Catalyst.Core.Modules.Sync.Tests\Catalyst.Core.Modules.Sync.Tests.csproj", "{D7D302E4-EF47-4212-BD50-13BEE85A6FB6}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Db.Rocks", "..\submodules\nethermind\src\Nethermind\Nethermind.Db.Rocks\Nethermind.Db.Rocks.csproj", "{E21D4DFA-26F0-486D-B345-700EF96C8ED0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RocksDbNative", "..\submodules\nethermind\src\rocksdb-sharp\RocksDbNative\RocksDbNative.csproj", "{8C464EDF-B72C-4A9B-84BB-1B51EC3A26A5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.Network.Dotnetty", "Catalyst.Modules.Network.Dotnetty\Catalyst.Modules.Network.Dotnetty.csproj", "{B528A317-D52C-4464-B9B3-CD5CB60835E5}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.Network.LibP2P", "Catalyst.Modules.Network.LibP2P\Catalyst.Modules.Network.LibP2P.csproj", "{1C337B50-79A6-4E7C-9529-2E0B8FC5CACC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.UPnP", "Catalyst.Modules.UPnP\Catalyst.Modules.UPnP.csproj", "{40E17E50-8D0C-43FE-AE1B-BEA0545C546D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.UPnP.Tests", "Catalyst.Modules.UPnP.Tests\Catalyst.Modules.UPnP.Tests.csproj", "{A9E06266-42C2-4074-949F-2DDA75F9A122}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Abi", "..\submodules\nethermind\src\Nethermind\Nethermind.Abi\Nethermind.Abi.csproj", "{9D218E65-D5AC-4EFC-B2E3-77F105AB9228}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Modules.Network.Dotnetty.Tests", "Catalyst.Modules.Network.Dotnetty.Tests\Catalyst.Modules.Network.Dotnetty.Tests.csproj", "{71FB9310-5741-4E99-B817-FA24C0BCA31E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Server", "Catalyst.Core.Modules.Rpc.Server\Catalyst.Core.Modules.Rpc.Server.csproj", "{4D932C0A-1EA5-419E-9DB5-60290BFB1D8B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Client", "Catalyst.Core.Modules.Rpc.Client\Catalyst.Core.Modules.Rpc.Client.csproj", "{C7F99ED7-9784-490C-9188-6D2E8D46E4DA}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Server.Tests", "Catalyst.Core.Modules.Rpc.Server.Tests\Catalyst.Core.Modules.Rpc.Server.Tests.csproj", "{C7E3BE5B-87A4-433C-BBE5-E8D11A5C500D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Catalyst.Tools.KeyGenerator", "Catalyst.Tools.KeyGenerator\Catalyst.Tools.KeyGenerator.csproj", "{2FEC7DC6-A6C1-4692-B5C3-1415B86DE761}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Core.Modules.Rpc.Client.Tests", "Catalyst.Core.Modules.Rpc.Client.Tests\Catalyst.Core.Modules.Rpc.Client.Tests.csproj", "{5FD08C1B-C3FF-4E48-AF96-852ED9F9B5BC}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Catalyst.Cli", "Catalyst.Cli\Catalyst.Cli.csproj", "{8B9E2D74-AD77-4B43-B7A5-5557428CC8C8}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Cli", "Catalyst.Cli\Catalyst.Cli.csproj", "{230B01FD-3231-4D38-8F3D-410CA84B3B90}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Catalyst.Cli.Tests", "Catalyst.Cli.Tests\Catalyst.Cli.Tests.csproj", "{5EEA2638-769D-4094-A417-0FE1396C1700}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Cli.Tests", "Catalyst.Cli.Tests\Catalyst.Cli.Tests.csproj", "{EF217648-56B7-4E96-B8DB-1C2C543B291D}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Catalyst.Node.POA.CE", "Catalyst.Node.POA.CE\Catalyst.Node.POA.CE.csproj", "{3750275D-F73D-4078-80C4-734BBD830A4E}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Catalyst.Module.ConvanSmartContract", "Catalyst.Module.ConvanSmartContract\Catalyst.Module.ConvanSmartContract.csproj", "{A9A0EA65-2236-46A5-BD1E-2A6242BF177C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Catalyst.Node.POA.CE.Tests", "Catalyst.Node.POA.CE.Tests\Catalyst.Node.POA.CE.Tests.csproj", "{36F415B5-4101-476A-9BD2-F18A5879F388}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Facade", "..\submodules\nethermind\src\Nethermind\Nethermind.Facade\Nethermind.Facade.csproj", "{31455838-ED29-4426-8328-D574CEBF2663}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Catalyst.Modules.Server.Blazor", "Catalyst.Modules.Server.Blazor\Catalyst.Modules.Server.Blazor.csproj", "{A2B05AD3-06DA-46E6-8662-DD9757F547B4}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Nethermind.Blockchain", "..\submodules\nethermind\src\Nethermind\Nethermind.Blockchain\Nethermind.Blockchain.csproj", "{10F3DDBF-4569-431D-B682-A67BA76D8166}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -190,10 +210,6 @@ Global {B276137D-FB34-48DB-80CF-ADA34A2D2F96}.Debug|Any CPU.Build.0 = Debug|Any CPU {B276137D-FB34-48DB-80CF-ADA34A2D2F96}.Release|Any CPU.ActiveCfg = Release|Any CPU {B276137D-FB34-48DB-80CF-ADA34A2D2F96}.Release|Any CPU.Build.0 = Release|Any CPU - {EC74EF99-CF2A-4F19-9BE1-0D0639F76C36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {EC74EF99-CF2A-4F19-9BE1-0D0639F76C36}.Debug|Any CPU.Build.0 = Debug|Any CPU - {EC74EF99-CF2A-4F19-9BE1-0D0639F76C36}.Release|Any CPU.ActiveCfg = Release|Any CPU - {EC74EF99-CF2A-4F19-9BE1-0D0639F76C36}.Release|Any CPU.Build.0 = Release|Any CPU {91A5F73C-5FE6-4BF3-B437-10FA9DF785E9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {91A5F73C-5FE6-4BF3-B437-10FA9DF785E9}.Debug|Any CPU.Build.0 = Debug|Any CPU {91A5F73C-5FE6-4BF3-B437-10FA9DF785E9}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -218,10 +234,6 @@ Global {A3386257-A47B-407B-AB5A-A2CBAF2CEDF9}.Debug|Any CPU.Build.0 = Debug|Any CPU {A3386257-A47B-407B-AB5A-A2CBAF2CEDF9}.Release|Any CPU.ActiveCfg = Release|Any CPU {A3386257-A47B-407B-AB5A-A2CBAF2CEDF9}.Release|Any CPU.Build.0 = Release|Any CPU - {F5BA5ECB-3C48-4379-9204-5BA30705D801}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F5BA5ECB-3C48-4379-9204-5BA30705D801}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F5BA5ECB-3C48-4379-9204-5BA30705D801}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F5BA5ECB-3C48-4379-9204-5BA30705D801}.Release|Any CPU.Build.0 = Release|Any CPU {6A2D2F12-3E0F-4784-A688-56DBB50AB955}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {6A2D2F12-3E0F-4784-A688-56DBB50AB955}.Debug|Any CPU.Build.0 = Debug|Any CPU {6A2D2F12-3E0F-4784-A688-56DBB50AB955}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -314,22 +326,6 @@ Global {7594E59C-115B-40E9-806A-F0FFF42B2894}.Debug|Any CPU.Build.0 = Debug|Any CPU {7594E59C-115B-40E9-806A-F0FFF42B2894}.Release|Any CPU.ActiveCfg = Release|Any CPU {7594E59C-115B-40E9-806A-F0FFF42B2894}.Release|Any CPU.Build.0 = Release|Any CPU - {4DE47B8A-A0C3-42C4-B00B-FFC3293D169F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4DE47B8A-A0C3-42C4-B00B-FFC3293D169F}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4DE47B8A-A0C3-42C4-B00B-FFC3293D169F}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4DE47B8A-A0C3-42C4-B00B-FFC3293D169F}.Release|Any CPU.Build.0 = Release|Any CPU - {D46348E9-850A-4654-AD62-D82E560A0CEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {D46348E9-850A-4654-AD62-D82E560A0CEB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {D46348E9-850A-4654-AD62-D82E560A0CEB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {D46348E9-850A-4654-AD62-D82E560A0CEB}.Release|Any CPU.Build.0 = Release|Any CPU - {C8414D13-5083-4EC8-92FC-4790C9966C7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {C8414D13-5083-4EC8-92FC-4790C9966C7B}.Debug|Any CPU.Build.0 = Debug|Any CPU - {C8414D13-5083-4EC8-92FC-4790C9966C7B}.Release|Any CPU.ActiveCfg = Release|Any CPU - {C8414D13-5083-4EC8-92FC-4790C9966C7B}.Release|Any CPU.Build.0 = Release|Any CPU - {93BE9DC8-E3E0-40CB-BD32-8A1170B88988}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {93BE9DC8-E3E0-40CB-BD32-8A1170B88988}.Debug|Any CPU.Build.0 = Debug|Any CPU - {93BE9DC8-E3E0-40CB-BD32-8A1170B88988}.Release|Any CPU.ActiveCfg = Release|Any CPU - {93BE9DC8-E3E0-40CB-BD32-8A1170B88988}.Release|Any CPU.Build.0 = Release|Any CPU {67AB76B4-4AA1-4259-95CA-CC0A6735D976}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {67AB76B4-4AA1-4259-95CA-CC0A6735D976}.Debug|Any CPU.Build.0 = Debug|Any CPU {67AB76B4-4AA1-4259-95CA-CC0A6735D976}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -382,18 +378,10 @@ Global {9B99940D-EBFA-40CC-9522-6776C687EAB3}.Debug|Any CPU.Build.0 = Debug|Any CPU {9B99940D-EBFA-40CC-9522-6776C687EAB3}.Release|Any CPU.ActiveCfg = Release|Any CPU {9B99940D-EBFA-40CC-9522-6776C687EAB3}.Release|Any CPU.Build.0 = Release|Any CPU - {2FEC7DC6-A6C1-4692-B5C3-1415B86DE761}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {2FEC7DC6-A6C1-4692-B5C3-1415B86DE761}.Debug|Any CPU.Build.0 = Debug|Any CPU - {2FEC7DC6-A6C1-4692-B5C3-1415B86DE761}.Release|Any CPU.ActiveCfg = Release|Any CPU - {2FEC7DC6-A6C1-4692-B5C3-1415B86DE761}.Release|Any CPU.Build.0 = Release|Any CPU - {8B9E2D74-AD77-4B43-B7A5-5557428CC8C8}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8B9E2D74-AD77-4B43-B7A5-5557428CC8C8}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8B9E2D74-AD77-4B43-B7A5-5557428CC8C8}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8B9E2D74-AD77-4B43-B7A5-5557428CC8C8}.Release|Any CPU.Build.0 = Release|Any CPU - {5EEA2638-769D-4094-A417-0FE1396C1700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {5EEA2638-769D-4094-A417-0FE1396C1700}.Debug|Any CPU.Build.0 = Debug|Any CPU - {5EEA2638-769D-4094-A417-0FE1396C1700}.Release|Any CPU.ActiveCfg = Release|Any CPU - {5EEA2638-769D-4094-A417-0FE1396C1700}.Release|Any CPU.Build.0 = Release|Any CPU + {EC7C0AAF-8EC0-491C-B98E-0EC9EBBA21CE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EC7C0AAF-8EC0-491C-B98E-0EC9EBBA21CE}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EC7C0AAF-8EC0-491C-B98E-0EC9EBBA21CE}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EC7C0AAF-8EC0-491C-B98E-0EC9EBBA21CE}.Release|Any CPU.Build.0 = Release|Any CPU {3750275D-F73D-4078-80C4-734BBD830A4E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3750275D-F73D-4078-80C4-734BBD830A4E}.Debug|Any CPU.Build.0 = Debug|Any CPU {3750275D-F73D-4078-80C4-734BBD830A4E}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -406,6 +394,142 @@ Global {A2B05AD3-06DA-46E6-8662-DD9757F547B4}.Debug|Any CPU.Build.0 = Debug|Any CPU {A2B05AD3-06DA-46E6-8662-DD9757F547B4}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2B05AD3-06DA-46E6-8662-DD9757F547B4}.Release|Any CPU.Build.0 = Release|Any CPU + {767E3E1E-43AF-4A7D-8638-84E81EB13F6E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {767E3E1E-43AF-4A7D-8638-84E81EB13F6E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {767E3E1E-43AF-4A7D-8638-84E81EB13F6E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {767E3E1E-43AF-4A7D-8638-84E81EB13F6E}.Release|Any CPU.Build.0 = Release|Any CPU + {F81AF4DE-30BC-48F3-B396-27087997B700}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F81AF4DE-30BC-48F3-B396-27087997B700}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F81AF4DE-30BC-48F3-B396-27087997B700}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F81AF4DE-30BC-48F3-B396-27087997B700}.Release|Any CPU.Build.0 = Release|Any CPU + {D6C18FBF-8D79-4817-940C-BF066E4E363A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D6C18FBF-8D79-4817-940C-BF066E4E363A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D6C18FBF-8D79-4817-940C-BF066E4E363A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D6C18FBF-8D79-4817-940C-BF066E4E363A}.Release|Any CPU.Build.0 = Release|Any CPU + {5BABC322-0C56-4477-978A-8EF49D7901B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5BABC322-0C56-4477-978A-8EF49D7901B3}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5BABC322-0C56-4477-978A-8EF49D7901B3}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5BABC322-0C56-4477-978A-8EF49D7901B3}.Release|Any CPU.Build.0 = Release|Any CPU + {76AF1E5A-94AC-44C5-B968-F816CB3A9B2E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {76AF1E5A-94AC-44C5-B968-F816CB3A9B2E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {76AF1E5A-94AC-44C5-B968-F816CB3A9B2E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {76AF1E5A-94AC-44C5-B968-F816CB3A9B2E}.Release|Any CPU.Build.0 = Release|Any CPU + {F6142130-599A-452E-92E9-348D0560E3E7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {F6142130-599A-452E-92E9-348D0560E3E7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {F6142130-599A-452E-92E9-348D0560E3E7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {F6142130-599A-452E-92E9-348D0560E3E7}.Release|Any CPU.Build.0 = Release|Any CPU + {43B92BEC-BE53-45D7-A442-77EB14122ED5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {43B92BEC-BE53-45D7-A442-77EB14122ED5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {43B92BEC-BE53-45D7-A442-77EB14122ED5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {43B92BEC-BE53-45D7-A442-77EB14122ED5}.Release|Any CPU.Build.0 = Release|Any CPU + {7DC55A67-EB89-4F6A-99D2-3E2F40D70738}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7DC55A67-EB89-4F6A-99D2-3E2F40D70738}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7DC55A67-EB89-4F6A-99D2-3E2F40D70738}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7DC55A67-EB89-4F6A-99D2-3E2F40D70738}.Release|Any CPU.Build.0 = Release|Any CPU + {6646C9CA-1E73-4738-A298-7672677B5209}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6646C9CA-1E73-4738-A298-7672677B5209}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6646C9CA-1E73-4738-A298-7672677B5209}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6646C9CA-1E73-4738-A298-7672677B5209}.Release|Any CPU.Build.0 = Release|Any CPU + {4DB10FC6-57AD-44AF-9623-4B1521EF5607}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4DB10FC6-57AD-44AF-9623-4B1521EF5607}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4DB10FC6-57AD-44AF-9623-4B1521EF5607}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4DB10FC6-57AD-44AF-9623-4B1521EF5607}.Release|Any CPU.Build.0 = Release|Any CPU + {6BEAE603-2C31-4D0D-8A69-434392BE6A1D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6BEAE603-2C31-4D0D-8A69-434392BE6A1D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {6BEAE603-2C31-4D0D-8A69-434392BE6A1D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6BEAE603-2C31-4D0D-8A69-434392BE6A1D}.Release|Any CPU.Build.0 = Release|Any CPU + {1B71B1A0-F7BC-44C5-970E-3205AA615853}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1B71B1A0-F7BC-44C5-970E-3205AA615853}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1B71B1A0-F7BC-44C5-970E-3205AA615853}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1B71B1A0-F7BC-44C5-970E-3205AA615853}.Release|Any CPU.Build.0 = Release|Any CPU + {65C36E26-6674-4457-A8FB-0F33EDA76759}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {65C36E26-6674-4457-A8FB-0F33EDA76759}.Debug|Any CPU.Build.0 = Debug|Any CPU + {65C36E26-6674-4457-A8FB-0F33EDA76759}.Release|Any CPU.ActiveCfg = Release|Any CPU + {65C36E26-6674-4457-A8FB-0F33EDA76759}.Release|Any CPU.Build.0 = Release|Any CPU + {DE28EED6-7281-4827-92C9-16B511BC617D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DE28EED6-7281-4827-92C9-16B511BC617D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DE28EED6-7281-4827-92C9-16B511BC617D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DE28EED6-7281-4827-92C9-16B511BC617D}.Release|Any CPU.Build.0 = Release|Any CPU + {36528129-62DF-4A4E-9337-85D51DA94EA4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {36528129-62DF-4A4E-9337-85D51DA94EA4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {36528129-62DF-4A4E-9337-85D51DA94EA4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {36528129-62DF-4A4E-9337-85D51DA94EA4}.Release|Any CPU.Build.0 = Release|Any CPU + {794B3026-4FEE-4AFC-AE05-7827BEA66E56}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {794B3026-4FEE-4AFC-AE05-7827BEA66E56}.Debug|Any CPU.Build.0 = Debug|Any CPU + {794B3026-4FEE-4AFC-AE05-7827BEA66E56}.Release|Any CPU.ActiveCfg = Release|Any CPU + {794B3026-4FEE-4AFC-AE05-7827BEA66E56}.Release|Any CPU.Build.0 = Release|Any CPU + {D7D302E4-EF47-4212-BD50-13BEE85A6FB6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D7D302E4-EF47-4212-BD50-13BEE85A6FB6}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D7D302E4-EF47-4212-BD50-13BEE85A6FB6}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D7D302E4-EF47-4212-BD50-13BEE85A6FB6}.Release|Any CPU.Build.0 = Release|Any CPU + {E21D4DFA-26F0-486D-B345-700EF96C8ED0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E21D4DFA-26F0-486D-B345-700EF96C8ED0}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E21D4DFA-26F0-486D-B345-700EF96C8ED0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E21D4DFA-26F0-486D-B345-700EF96C8ED0}.Release|Any CPU.Build.0 = Release|Any CPU + {8C464EDF-B72C-4A9B-84BB-1B51EC3A26A5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8C464EDF-B72C-4A9B-84BB-1B51EC3A26A5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8C464EDF-B72C-4A9B-84BB-1B51EC3A26A5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8C464EDF-B72C-4A9B-84BB-1B51EC3A26A5}.Release|Any CPU.Build.0 = Release|Any CPU + {B528A317-D52C-4464-B9B3-CD5CB60835E5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B528A317-D52C-4464-B9B3-CD5CB60835E5}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B528A317-D52C-4464-B9B3-CD5CB60835E5}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B528A317-D52C-4464-B9B3-CD5CB60835E5}.Release|Any CPU.Build.0 = Release|Any CPU + {1C337B50-79A6-4E7C-9529-2E0B8FC5CACC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1C337B50-79A6-4E7C-9529-2E0B8FC5CACC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {1C337B50-79A6-4E7C-9529-2E0B8FC5CACC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1C337B50-79A6-4E7C-9529-2E0B8FC5CACC}.Release|Any CPU.Build.0 = Release|Any CPU + {40E17E50-8D0C-43FE-AE1B-BEA0545C546D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40E17E50-8D0C-43FE-AE1B-BEA0545C546D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40E17E50-8D0C-43FE-AE1B-BEA0545C546D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40E17E50-8D0C-43FE-AE1B-BEA0545C546D}.Release|Any CPU.Build.0 = Release|Any CPU + {A9E06266-42C2-4074-949F-2DDA75F9A122}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9E06266-42C2-4074-949F-2DDA75F9A122}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9E06266-42C2-4074-949F-2DDA75F9A122}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9E06266-42C2-4074-949F-2DDA75F9A122}.Release|Any CPU.Build.0 = Release|Any CPU + {9D218E65-D5AC-4EFC-B2E3-77F105AB9228}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {9D218E65-D5AC-4EFC-B2E3-77F105AB9228}.Debug|Any CPU.Build.0 = Debug|Any CPU + {9D218E65-D5AC-4EFC-B2E3-77F105AB9228}.Release|Any CPU.ActiveCfg = Release|Any CPU + {9D218E65-D5AC-4EFC-B2E3-77F105AB9228}.Release|Any CPU.Build.0 = Release|Any CPU + {71FB9310-5741-4E99-B817-FA24C0BCA31E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {71FB9310-5741-4E99-B817-FA24C0BCA31E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {71FB9310-5741-4E99-B817-FA24C0BCA31E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {71FB9310-5741-4E99-B817-FA24C0BCA31E}.Release|Any CPU.Build.0 = Release|Any CPU + {4D932C0A-1EA5-419E-9DB5-60290BFB1D8B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4D932C0A-1EA5-419E-9DB5-60290BFB1D8B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4D932C0A-1EA5-419E-9DB5-60290BFB1D8B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4D932C0A-1EA5-419E-9DB5-60290BFB1D8B}.Release|Any CPU.Build.0 = Release|Any CPU + {C7F99ED7-9784-490C-9188-6D2E8D46E4DA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7F99ED7-9784-490C-9188-6D2E8D46E4DA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7F99ED7-9784-490C-9188-6D2E8D46E4DA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7F99ED7-9784-490C-9188-6D2E8D46E4DA}.Release|Any CPU.Build.0 = Release|Any CPU + {C7E3BE5B-87A4-433C-BBE5-E8D11A5C500D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7E3BE5B-87A4-433C-BBE5-E8D11A5C500D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {C7E3BE5B-87A4-433C-BBE5-E8D11A5C500D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7E3BE5B-87A4-433C-BBE5-E8D11A5C500D}.Release|Any CPU.Build.0 = Release|Any CPU + {5FD08C1B-C3FF-4E48-AF96-852ED9F9B5BC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5FD08C1B-C3FF-4E48-AF96-852ED9F9B5BC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5FD08C1B-C3FF-4E48-AF96-852ED9F9B5BC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5FD08C1B-C3FF-4E48-AF96-852ED9F9B5BC}.Release|Any CPU.Build.0 = Release|Any CPU + {230B01FD-3231-4D38-8F3D-410CA84B3B90}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {230B01FD-3231-4D38-8F3D-410CA84B3B90}.Debug|Any CPU.Build.0 = Debug|Any CPU + {230B01FD-3231-4D38-8F3D-410CA84B3B90}.Release|Any CPU.ActiveCfg = Release|Any CPU + {230B01FD-3231-4D38-8F3D-410CA84B3B90}.Release|Any CPU.Build.0 = Release|Any CPU + {EF217648-56B7-4E96-B8DB-1C2C543B291D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {EF217648-56B7-4E96-B8DB-1C2C543B291D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {EF217648-56B7-4E96-B8DB-1C2C543B291D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {EF217648-56B7-4E96-B8DB-1C2C543B291D}.Release|Any CPU.Build.0 = Release|Any CPU + {A9A0EA65-2236-46A5-BD1E-2A6242BF177C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A9A0EA65-2236-46A5-BD1E-2A6242BF177C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A9A0EA65-2236-46A5-BD1E-2A6242BF177C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A9A0EA65-2236-46A5-BD1E-2A6242BF177C}.Release|Any CPU.Build.0 = Release|Any CPU + {31455838-ED29-4426-8328-D574CEBF2663}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {31455838-ED29-4426-8328-D574CEBF2663}.Debug|Any CPU.Build.0 = Debug|Any CPU + {31455838-ED29-4426-8328-D574CEBF2663}.Release|Any CPU.ActiveCfg = Release|Any CPU + {31455838-ED29-4426-8328-D574CEBF2663}.Release|Any CPU.Build.0 = Release|Any CPU + {10F3DDBF-4569-431D-B682-A67BA76D8166}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {10F3DDBF-4569-431D-B682-A67BA76D8166}.Debug|Any CPU.Build.0 = Debug|Any CPU + {10F3DDBF-4569-431D-B682-A67BA76D8166}.Release|Any CPU.ActiveCfg = Release|Any CPU + {10F3DDBF-4569-431D-B682-A67BA76D8166}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -416,11 +540,21 @@ Global {CEC977D9-D46F-4523-9941-99BCF6B93EFE} = {6074E807-FBBC-41D0-9205-C7B79513D683} {9EED8D3C-F0EC-4D07-9021-5B1D8D7F11C2} = {6074E807-FBBC-41D0-9205-C7B79513D683} {A3386257-A47B-407B-AB5A-A2CBAF2CEDF9} = {6074E807-FBBC-41D0-9205-C7B79513D683} - {F5BA5ECB-3C48-4379-9204-5BA30705D801} = {6074E807-FBBC-41D0-9205-C7B79513D683} - {AA88D78E-377B-4452-864A-C1D5DFB06854} = {3339A04C-D933-4712-A556-F8F1B74B9753} - {46B22170-3D54-43F4-9474-C9F02DEF3A14} = {3339A04C-D933-4712-A556-F8F1B74B9753} {70AF0C8D-A405-48A1-9914-7B6AAC66F1E2} = {6074E807-FBBC-41D0-9205-C7B79513D683} - {BAB02934-9818-4691-91AF-8B56980C82C7} = {3339A04C-D933-4712-A556-F8F1B74B9753} + {43B92BEC-BE53-45D7-A442-77EB14122ED5} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {7DC55A67-EB89-4F6A-99D2-3E2F40D70738} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {6646C9CA-1E73-4738-A298-7672677B5209} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {4DB10FC6-57AD-44AF-9623-4B1521EF5607} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {6BEAE603-2C31-4D0D-8A69-434392BE6A1D} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {1B71B1A0-F7BC-44C5-970E-3205AA615853} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {65C36E26-6674-4457-A8FB-0F33EDA76759} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {DE28EED6-7281-4827-92C9-16B511BC617D} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {36528129-62DF-4A4E-9337-85D51DA94EA4} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {E21D4DFA-26F0-486D-B345-700EF96C8ED0} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {8C464EDF-B72C-4A9B-84BB-1B51EC3A26A5} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {9D218E65-D5AC-4EFC-B2E3-77F105AB9228} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {31455838-ED29-4426-8328-D574CEBF2663} = {6074E807-FBBC-41D0-9205-C7B79513D683} + {10F3DDBF-4569-431D-B682-A67BA76D8166} = {6074E807-FBBC-41D0-9205-C7B79513D683} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {34F221DE-645E-4697-BCC2-818F1F10FE8A} diff --git a/src/Catalyst.sln.DotSettings b/src/Catalyst.sln.DotSettings deleted file mode 100644 index c3bca28415..0000000000 --- a/src/Catalyst.sln.DotSettings +++ /dev/null @@ -1,431 +0,0 @@ - - // Use the following placeholders: -// $EXPR$ -- source expression -// $NAME$ -- source name (string literal or 'nameof' expression) -// $MESSAGE$ -- string literal in the form of "$NAME$ != null" -Guard.Argument($EXPR$, $NAME$).NotNull() - ExplicitlyExcluded - ExplicitlyExcluded - - True - ExplicitlyExcluded - ExplicitlyExcluded - ExplicitlyExcluded - ExplicitlyExcluded - ExplicitlyExcluded - ExplicitlyExcluded - True - WARNING - WARNING - WARNING - WARNING - - True - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - WARNING - WARNING - - True - - True - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - ERROR - WARNING - WARNING - ERROR - ERROR - ERROR - ERROR - - True - ERROR - ERROR - - True - ERROR - ERROR - ERROR - ERROR - SUGGESTION - ERROR - ERROR - WARNING - ERROR - True - False - 1 - TOGETHER_SAME_LINE - INSIDE - INSIDE - INSIDE - INSIDE - INSIDE - INSIDE - True - 1 - 1 - True - False - False - True - True - True - True - NEVER - ALWAYS - True - True - False - False - CHOP_IF_LONG - UTC - False - <Policy Inspect="True" Prefix="" Suffix="" Style="AaBb_AaBb" /> - - Detailed - True - True - True - True - True - True - True - True - True - True - Always - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - True - - True - True - MessageHandler - cs - True - True - True - InCSharpProjectFile - True - fileDefaultNamespace() - -1 - 0 - True - 1 - True - True - True - True - True - True - &Class - #region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node <https://github.com/catalyst-network/Catalyst.Node> -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see <https://www.gnu.org/licenses/>. -*/ - -#endregion - -namespace $NAMESPACE$ -{ - public class $CLASS$ {$END$} -} - True - True - Class - cs - True - True - True - InCSharpProjectFile - True - getAlphaNumericFileNameWithoutExtension() - -1 - 0 - True - fileDefaultNamespace() - -1 - 1 - True - &Interface - #region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node <https://github.com/catalyst-network/Catalyst.Node> -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see <https://www.gnu.org/licenses/>. -*/ - -#endregion - -namespace $NAMESPACE$ -{ - public interface $INTERFACE$ {$END$} -} - True - True - Interface - cs - True - True - True - InCSharpProjectFile - True - getAlphaNumericFileNameWithoutExtension() - -1 - 0 - True - fileDefaultNamespace() - -1 - 1 - True - Test - #region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node <https://github.com/catalyst-network/Catalyst.Node> -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see <https://www.gnu.org/licenses/>. -*/ - -#endregion - -using FluentAssertions; -using NSubstitute; -using Xunit; - -namespace $NAMESPACE$ -{ - public class $CLASS$ - { - [Fact] - public void MyTestedMethod_Should_Be_Producing_This_Result_When_Some_Conditions_Are_Met() - { - true.Should().BeTrue(); - } - } -} - True - True - Tests - cs - True - True - True - InCSharpProjectFile - True - getAlphaNumericFileNameWithoutExtension() - -1 - 0 - True - fileDefaultNamespace() - -1 - 1 - True - &Struct - #region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node <https://github.com/catalyst-network/Catalyst.Node> -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see <https://www.gnu.org/licenses/>. -*/ - -#endregion - -namespace $NAMESPACE$ -{ - public struct $STRUCT$ {$END$} -} - True - True - Struct - cs - True - True - True - InCSharpProjectFile - True - getAlphaNumericFileNameWithoutExtension() - -1 - 0 - True - fileDefaultNamespace() - -1 - 1 - True - &Enum - #region LICENSE - -/** -* Copyright (c) 2019 Catalyst Network -* -* This file is part of Catalyst.Node <https://github.com/catalyst-network/Catalyst.Node> -* -* Catalyst.Node is free software: you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation, either version 2 of the License, or -* (at your option) any later version. -* -* Catalyst.Node is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with Catalyst.Node. If not, see <https://www.gnu.org/licenses/>. -*/ - -#endregion - -namespace $NAMESPACE$ -{ - public enum $ENUM$ {$END$} -} - True - True - Enum - cs - True - True - True - InCSharpProjectFile - True - getAlphaNumericFileNameWithoutExtension() - -1 - 0 - True - fileDefaultNamespace() - -1 - 1 - True - True - - diff --git a/src/CatalystTestPlan.md b/src/CatalystTestPlan.md new file mode 100644 index 0000000000..f8ee5cd4f5 --- /dev/null +++ b/src/CatalystTestPlan.md @@ -0,0 +1,46 @@ +# Catalyst Test Plan +> This Test Plan describes the integration and system tests that will be conducted on Catalyst following integration of the subsystems and components identified. + +> It is assumed that unit testing already provided thorough black box testing, extensive coverage of source code, and testing of all module interfaces. + +> The testing will revolve around Catalyst features in a live network, performance tests are currently excluded from this test plan. + +The following interfaces will be tested or used for testing: +- Catalyst Node + +- Catalyst Dashboard + +- Truffle - HDWalletProvider + +> The tests should be conducted on both local and remote computers. + +## Test Cases + +### Consensus +To test the consensus is working correctly - start a few POA nodes and let them produce new deltas, every node should eventually produce a new delta as long as it is a POA node. The cycle’s should always be in-sync and the delta production time should always be +19 seconds. +> You can view the delta production in the block explorer. + +### Synchronization +To test synchronization with other remote peers: +Start a few POA nodes and let them run and participate in consensus until over a hundred blocks are generated. + +POA Node - Start a new POA node until it synchronizes to the current block height of the other nodes, if it completes and continues to participate in consensus(Receives deltas from other nodes and produces its own deltas to other nodes) it has successfully synchronized. + +Non POA Node - Start a new non POA node wait until it synchronizes to the current block height of the other nodes, if it completes and continues receiving new blocks every cycle the synchronization process has successfully completed. + +### Transactions/Smart Contracts +The truffle tests will generate plain transactions as well as smart contract transactions. +To test transactions and smart contracts: + +Edit the file "src\Catalyst.Core.Modules.Ledger.Tests\IntegrationTests\TruffleTest\truffle-config.js" and point the catalyst endpoint to the node you would like to test. Run the tuffle tests from console: +```truffle test --network catalyst``` + +You should eventually receive transactions that contain plain transactions as well as smart contract transactions. + +### Web3 +The Web3 controller has two main functions, an interface to both the DFS and EVM as well as custom controllers created by the node operator. +To test the DFS and custom controllers this can be done through the swagger endpoint at: `http://[IPAddress]:5005/swagger/index.html` + +> To test the EVM json rpc this can be tested at: `http://[IPAddress]:5005/api/eth/request` + +> For a list of eth json rpc calls you can use the following site: `https://infura.io/docs/ethereum/json-rpc/eth-call` diff --git a/src/Common.Projects.props b/src/Common.Projects.props index cc098e7335..6328b6d266 100644 --- a/src/Common.Projects.props +++ b/src/Common.Projects.props @@ -1,13 +1,13 @@ catalystnet.org - Copyright © 2019 Catalyst Network + Copyright © 2022 Catalyst Network https://github.com/organizations/catalyst-network/settings/profile http://opensource.org/licenses/GPL-3.0 https://github.com/Catalyst-network/Catalyst.Node git - 3.0.0 - 7.3 + 6.0.0 + latest true true AnyCPU diff --git a/src/Common.TestProjects.props b/src/Common.TestProjects.props index 8f6e3c757e..cb9aae069d 100644 --- a/src/Common.TestProjects.props +++ b/src/Common.TestProjects.props @@ -6,27 +6,24 @@ false - + - - - - - - - - - + + + + + + - + all runtime; build; native; contentfiles; analyzers - - - PreserveNewest - - + + + + + diff --git a/src/Lib.P2P.Tests/App.config b/src/Lib.P2P.Tests/App.config new file mode 100644 index 0000000000..74973e4c27 --- /dev/null +++ b/src/Lib.P2P.Tests/App.config @@ -0,0 +1,20 @@ + + + + +
+ + + + + + + + + + + + + + + diff --git a/src/Lib.P2P.Tests/AutoDialerTest.cs b/src/Lib.P2P.Tests/AutoDialerTest.cs new file mode 100644 index 0000000000..5108110555 --- /dev/null +++ b/src/Lib.P2P.Tests/AutoDialerTest.cs @@ -0,0 +1,203 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests +{ + [TestClass] + public sealed class AutoDialerTest + { + private Peer peerA = new Peer + { + AgentVersion = "A", + Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCC5r4nQBtnd9qgjnG8fBN5+gnqIeWEIcUFUdCG4su/vrbQ1py8XGKNUBuDjkyTv25Gd3hlrtNJV3eOKZVSL8ePAgMBAAE=" + }; + + private Peer peerB = new Peer + { + AgentVersion = "B", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + + private Peer peerC = new Peer + { + AgentVersion = "C", + Id = "QmTcEBjSTSLjeu2oTiSoBSQQgqH5MADUsemXewn6rThoDT", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCAL8J1Lp6Ad5eYanOwNenXZ6Efvhk9wwFRXqqPn9UT+/JTxBvZPzQwK/FbPRczjZ/A1x8BSec1gvFCzcX4fkULAgMBAAE=" + }; + + [TestMethod] + public void Defaults() + { + using (var dialer = new AutoDialer(new SwarmService(peerA))) + { + Assert.AreEqual(AutoDialer.DefaultMinConnections, dialer.MinConnections); + } + } + + [TestMethod] + public async Task Connects_OnPeerDiscovered_When_Below_MinConnections() + { + var swarmA = new SwarmService(peerA); + await swarmA.StartAsync(); + await swarmA.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var swarmB = new SwarmService(peerB); + await swarmB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + try + { + using (new AutoDialer(swarmA)) + { + var other = swarmA.RegisterPeerAddress(peerBAddress); + + // wait for the connection. + var endTime = DateTime.Now.AddSeconds(3); + while (other.ConnectedAddress == null) + { + if (DateTime.Now > endTime) + Assert.Fail("Did not do autodial"); + await Task.Delay(100); + } + } + } + finally + { + await swarmA?.StopAsync(); + await swarmB?.StopAsync(); + } + } + + [TestMethod] + public async Task Noop_OnPeerDiscovered_When_NotBelow_MinConnections() + { + var swarmA = new SwarmService(peerA); + await swarmA.StartAsync(); + await swarmA.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var swarmB = new SwarmService(peerB); + await swarmB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + try + { + using (new AutoDialer(swarmA) {MinConnections = 0}) + { + var other = swarmA.RegisterPeerAddress(peerBAddress); + + // wait for the connection. + var endTime = DateTime.Now.AddSeconds(3); + while (other.ConnectedAddress == null) + { + if (DateTime.Now > endTime) + { + return; + } + + await Task.Delay(100); + } + + Assert.Fail("Autodial should not happen"); + } + } + finally + { + await swarmA.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public async Task Connects_OnPeerDisconnected_When_Below_MinConnections() + { + var swarmA = new SwarmService(peerA); + await swarmA.StartAsync(); + await swarmA.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var swarmB = new SwarmService(peerB); + await swarmB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var swarmC = new SwarmService(peerC); + await swarmC.StartAsync(); + var peerCAddress = await swarmC.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var isBConnected = false; + swarmA.ConnectionEstablished += (s, conn) => + { + if (conn.RemotePeer == peerB) + { + isBConnected = true; + } + }; + + try + { + using (new AutoDialer(swarmA) {MinConnections = 1}) + { + swarmA.RegisterPeerAddress(peerBAddress); + var c = swarmA.RegisterPeerAddress(peerCAddress); + + // wait for the peer B connection. + var endTime = DateTime.Now.AddSeconds(3); + while (!isBConnected) + { + if (DateTime.Now > endTime) + { + Assert.Fail("Did not do autodial on peer discovered"); + } + + await Task.Delay(100); // get cancellaton token + } + + Assert.IsNull(c.ConnectedAddress); + await swarmA.DisconnectAsync(peerBAddress); + + // wait for the peer C connection. + endTime = DateTime.Now.AddSeconds(3); + while (c.ConnectedAddress == null) + { + if (DateTime.Now > endTime) + Assert.Fail("Did not do autodial on peer disconnected"); + await Task.Delay(100); + } + } + } + finally + { + await swarmA?.StopAsync(); + await swarmB?.StopAsync(); + await swarmC?.StopAsync(); + } + } + } +} diff --git a/src/Lib.P2P.Tests/BlackListTest.cs b/src/Lib.P2P.Tests/BlackListTest.cs new file mode 100644 index 0000000000..dcefd37a91 --- /dev/null +++ b/src/Lib.P2P.Tests/BlackListTest.cs @@ -0,0 +1,50 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests +{ + [TestClass] + public class BlackListTest + { + [TestMethod] + public void Allowed() + { + var policy = new BlackList(); + policy.Add("c"); + policy.Add("d"); + Assert.IsTrue(policy.IsAllowed("a")); + Assert.IsTrue(policy.IsAllowed("b")); + Assert.IsFalse(policy.IsAllowed("c")); + Assert.IsFalse(policy.IsAllowed("d")); + } + + [TestMethod] + public void Empty() + { + var policy = new BlackList(); + Assert.IsTrue(policy.IsAllowed("a")); + } + } +} diff --git a/src/Lib.P2P.Tests/CidTest.cs b/src/Lib.P2P.Tests/CidTest.cs new file mode 100644 index 0000000000..14e9b5034e --- /dev/null +++ b/src/Lib.P2P.Tests/CidTest.cs @@ -0,0 +1,485 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Google.Protobuf; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; +using Newtonsoft.Json; + +namespace Lib.P2P.Tests +{ + [TestClass] + public class CidTest + { + [TestMethod] + public void ToString_Default() + { + var cid = new Cid {Hash = new MultiHash("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V")}; + Assert.AreEqual("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", cid.ToString()); + + cid = "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + Assert.AreEqual( + "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67", + cid.ToString()); + } + + [TestMethod] + public void ToString_L() + { + var cid = new Cid {Hash = new MultiHash("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V")}; + Assert.AreEqual("base58btc cidv0 dag-pb sha2-256 QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", + cid.ToString("L")); + + cid = "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + Assert.AreEqual( + "base58btc cidv1 dag-pb sha2-512 8Vx9QNCcSt39anEamkkSaNw5rDHQ7yuadq7ihZed477qQNXxYr3HReMamd1Q2EnUeL4oNtVAmNw1frEhEN1aoqFuKD", + cid.ToString("L")); + } + + [TestMethod] + public void ToString_G() + { + var cid = new Cid {Hash = new MultiHash("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V")}; + Assert.AreEqual("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", cid.ToString("G")); + + cid = "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + Assert.AreEqual( + "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67", + cid.ToString("G")); + } + + [TestMethod] + public void ToString_InvalidFormat() + { + var cid = new Cid {Hash = new MultiHash("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V")}; + ExceptionAssert.Throws(() => cid.ToString("?")); + } + + [TestMethod] + public void MultiHash_is_Cid_V0() + { + var mh = new MultiHash("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V"); + Cid cid = mh; + Assert.AreEqual(0, cid.Version); + Assert.AreEqual("dag-pb", cid.ContentType); + Assert.AreEqual("base58btc", cid.Encoding); + Assert.AreSame(mh, cid.Hash); + } + + [TestMethod] + public void MultiHash_is_Cid_V1() + { + var hello = Encoding.UTF8.GetBytes("Hello, world."); + var mh = MultiHash.ComputeHash(hello, "sha2-512"); + Cid cid = mh; + Assert.AreEqual(1, cid.Version); + Assert.AreEqual("dag-pb", cid.ContentType); + Assert.AreEqual("base32", cid.Encoding); + Assert.AreSame(mh, cid.Hash); + } + + [TestMethod] + public void Encode_V0() + { + var hash = "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V"; + Cid cid = new MultiHash(hash); + Assert.AreEqual(hash, cid.Encode()); + Assert.AreEqual(0, cid.Version); + + cid = new Cid + { + ContentType = "dag-pb", + Encoding = "base58btc", + Hash = hash + }; + Assert.AreEqual(hash, cid.Encode()); + Assert.AreEqual(0, cid.Version); + } + + [TestMethod] + public void Encode_V1() + { + var cid = new Cid + { + Version = 1, + ContentType = "raw", + Encoding = "base58btc", + Hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4" + }; + Assert.AreEqual("zb2rhj7crUKTQYRGCRATFaQ6YFLTde2YzdqbbhAASkL9uRDXn", cid.Encode()); + + cid = new Cid + { + ContentType = "raw", + Hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4" + }; + Assert.AreEqual(1, cid.Version); + Assert.AreEqual("base32", cid.Encoding); + Assert.AreEqual("bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e", cid.Encode()); + } + + [TestMethod] + public void Encode_Upgrade_to_V1_ContentType() + { + var cid = new Cid + { + ContentType = "raw", + Hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4" + }; + Assert.AreEqual(1, cid.Version); + Assert.AreEqual("base32", cid.Encoding); + Assert.AreEqual("bafkreifzjut3te2nhyekklss27nh3k72ysco7y32koao5eei66wof36n5e", cid.Encode()); + } + + [TestMethod] + public void Encode_Upgrade_to_V1_Encoding() + { + var cid = new Cid + { + Encoding = "base64", + Hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4" + }; + Assert.AreEqual(1, cid.Version); + Assert.AreEqual("mAXASILlNJ7mTTT4IpS5S19p9q/rEhO/jelOA7pCI96zi783p", cid.Encode()); + } + + [TestMethod] + public void Encode_Upgrade_to_V1_Hash() + { + var hello = Encoding.UTF8.GetBytes("Hello, world."); + var mh = MultiHash.ComputeHash(hello, "sha2-512"); + var cid = new Cid + { + Hash = mh + }; + Assert.AreEqual(1, cid.Version); + Assert.AreEqual("base32", cid.Encoding); + Assert.AreEqual( + "bafybgqfnbq34ghljwmk7hka7cpem3zybbffnsfzfxinq3qyztsuxcntbxaua23xx42hrgptcchrolkndcucelv3pc4eoarjbwdxagtylboxsm", + cid.Encode()); + } + + [TestMethod] + public void Encode_V1_Invalid_ContentType() + { + var cid = new Cid + { + Version = 1, + ContentType = "unknown", + Encoding = "base58btc", + Hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4" + }; + Assert.ThrowsException(() => cid.Encode()); + } + + [TestMethod] + public void Encode_V1_Invalid_Encoding() + { + var cid = new Cid + { + Version = 1, + ContentType = "raw", + Encoding = "unknown", + Hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4" + }; + Assert.ThrowsException(() => cid.Encode()); + } + + [TestMethod] + public void Decode_V0() + { + var hash = "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V"; + var cid = Cid.Decode(hash); + Assert.AreEqual(0, cid.Version); + Assert.AreEqual("dag-pb", cid.ContentType); + Assert.AreEqual("base58btc", cid.Encoding); + Assert.AreEqual(hash, cid.Encode()); + } + + [TestMethod] + public void Decode_V0_Invalid() + { + var hash = "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39?"; + Assert.ThrowsException(() => Cid.Decode(hash)); + } + + [TestMethod] + public void Decode_Invalid_Version() + { + var cid = new Cid + { + Version = 32767, + ContentType = "raw", + Encoding = "base58btc", + Hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4" + }; + var s = cid.Encode(); + Assert.ThrowsException(() => Cid.Decode(s)); + } + + [TestMethod] + public void Decode_V1() + { + var id = "zb2rhj7crUKTQYRGCRATFaQ6YFLTde2YzdqbbhAASkL9uRDXn"; + var hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"; + var cid = Cid.Decode(id); + Assert.AreEqual(1, cid.Version); + Assert.AreEqual("base58btc", cid.Encoding); + Assert.AreEqual("raw", cid.ContentType); + Assert.AreEqual(hash, cid.Hash); + } + + [TestMethod] + public void Decode_V1_Unknown_ContentType() + { + var id = "zJAFhtPN28kqMxDkZawWCCL52BzaiymqFgX3LA7XzkNRMNAN1T1J"; + var hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"; + var cid = Cid.Decode(id); + Assert.AreEqual(1, cid.Version); + Assert.AreEqual("base58btc", cid.Encoding); + Assert.AreEqual("codec-32767", cid.ContentType); + Assert.AreEqual(hash, cid.Hash); + } + + [TestMethod] + public void Decode_V1_Invalid_MultiBase_String() + { + var id = "zb2rhj7crUKTQYRGCRATFaQ6YFLTde2YzdqbbhAASkL9uRDX?"; + Assert.ThrowsException(() => Cid.Decode(id)); + } + + [TestMethod] + public void Decode_V1_Invalid_MultiBase_Code() + { + var id = "?"; + Assert.ThrowsException(() => Cid.Decode(id)); + } + + [TestMethod] + public void Value_Equality() + { + var a0 = Cid.Decode("zb2rhj7crUKTQYRGCRATFaQ6YFLTde2YzdqbbhAASkL9uRDXn"); + var a1 = Cid.Decode("zb2rhj7crUKTQYRGCRATFaQ6YFLTde2YzdqbbhAASkL9uRDXn"); + var b = Cid.Decode("QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L5"); + Cid c = null; + Cid d = null; + + Assert.IsTrue(c == d); + Assert.IsFalse(c == b); + Assert.IsFalse(b == c); + + Assert.IsFalse(c != d); + Assert.IsTrue(c != b); + Assert.IsTrue(b != c); + +#pragma warning disable 1718 + Assert.IsTrue(a0 == a0); + Assert.IsTrue(a0 == a1); + Assert.IsFalse(a0 == b); + + Assert.IsFalse(a0 != a0); + Assert.IsFalse(a0 != a1); + Assert.IsTrue(a0 != b); + + Assert.IsTrue(a0.Equals(a0)); + Assert.IsTrue(a0.Equals(a1)); + Assert.IsFalse(a0.Equals(b)); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0.GetHashCode(), a0.GetHashCode()); + Assert.AreEqual(a0.GetHashCode(), a1.GetHashCode()); + Assert.AreNotEqual(a0.GetHashCode(), b.GetHashCode()); + } + + [TestMethod] + public void Implicit_Conversion_From_V0_String() + { + var hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"; + Cid cid = hash; + Assert.AreEqual(0, cid.Version); + Assert.AreEqual("dag-pb", cid.ContentType); + Assert.AreEqual("base58btc", cid.Encoding); + Assert.AreEqual(hash, cid.Encode()); + } + + [TestMethod] + public void Implicit_Conversion_From_V1_String() + { + var id = "zb2rhj7crUKTQYRGCRATFaQ6YFLTde2YzdqbbhAASkL9uRDXn"; + var hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"; + Cid cid = id; + Assert.AreEqual(1, cid.Version); + Assert.AreEqual("base58btc", cid.Encoding); + Assert.AreEqual("raw", cid.ContentType); + Assert.AreEqual(hash, cid.Hash); + } + + [TestMethod] + public void Implicit_Conversion_To_String() + { + var id = "zb2rhj7crUKTQYRGCRATFaQ6YFLTde2YzdqbbhAASkL9uRDXn"; + Cid cid = id; + string s = cid; + Assert.AreEqual(id, s); + } + + [TestMethod] + public void Streaming_V0() + { + Cid cid = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"; + var stream = new MemoryStream(); + cid.Write(stream); + stream.Position = 0; + var clone = Cid.Read(stream); + Assert.AreEqual(cid.Version, clone.Version); + Assert.AreEqual(cid.ContentType, clone.ContentType); + Assert.AreEqual(cid.Hash, clone.Hash); + } + + [TestMethod] + public void Streaming_V1() + { + Cid cid = "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + var stream = new MemoryStream(); + cid.Write(stream); + stream.Position = 0; + var clone = Cid.Read(stream); + Assert.AreEqual(cid.Version, clone.Version); + Assert.AreEqual(cid.ContentType, clone.ContentType); + Assert.AreEqual(cid.Hash, clone.Hash); + } + + [TestMethod] + public void Protobuf_V0() + { + Cid cid = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"; + var stream = new MemoryStream(); + var cos = new CodedOutputStream(stream); + cid.Write(cos); + cos.Flush(); + stream.Position = 0; + var cis = new CodedInputStream(stream); + var clone = Cid.Read(cis); + Assert.AreEqual(cid.Version, clone.Version); + Assert.AreEqual(cid.ContentType, clone.ContentType); + Assert.AreEqual(cid.Hash, clone.Hash); + } + + [TestMethod] + public void Protobuf_V1() + { + Cid cid = "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + var stream = new MemoryStream(); + var cos = new CodedOutputStream(stream); + cid.Write(cos); + cos.Flush(); + stream.Position = 0; + var cis = new CodedInputStream(stream); + var clone = Cid.Read(cis); + Assert.AreEqual(cid.Version, clone.Version); + Assert.AreEqual(cid.ContentType, clone.ContentType); + Assert.AreEqual(cid.Hash, clone.Hash); + } + + [TestMethod] + public void Immutable() + { + Cid cid = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"; + Assert.AreEqual("QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4", cid.Encode()); + ExceptionAssert.Throws(() => cid.ContentType = "dag-cbor"); + ExceptionAssert.Throws(() => cid.Encoding = "base64"); + ExceptionAssert.Throws(() => + cid.Hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L5"); + ExceptionAssert.Throws(() => cid.Version = 0); + } + + private sealed class CidAndX + { + public Cid Cid; + public int X; + } + + [TestMethod] + public void JsonSerialization() + { + Cid a = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"; + var json = JsonConvert.SerializeObject(a); + Assert.AreEqual($"\"{a.Encode()}\"", json); + var b = JsonConvert.DeserializeObject(json); + Assert.AreEqual(a, b); + + a = null; + json = JsonConvert.SerializeObject(a); + b = JsonConvert.DeserializeObject(json); + Assert.IsNull(b); + + var x = new CidAndX {Cid = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4", X = 42}; + json = JsonConvert.SerializeObject(x); + var y = JsonConvert.DeserializeObject(json); + Assert.AreEqual(x.Cid, y.Cid); + Assert.AreEqual(x.X, y.X); + + x.Cid = null; + json = JsonConvert.SerializeObject(x); + y = JsonConvert.DeserializeObject(json); + Assert.AreEqual(x.Cid, y.Cid); + Assert.AreEqual(x.X, y.X); + } + + [TestMethod] + public void ByteArrays_V1() + { + Cid cid = "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + var buffer = cid.ToArray(); + var clone = Cid.Read(buffer); + Assert.AreEqual(cid.Version, clone.Version); + Assert.AreEqual(cid.ContentType, clone.ContentType); + Assert.AreEqual(cid.Hash.Algorithm.Name, clone.Hash.Algorithm.Name); + Assert.AreEqual(cid.Hash, clone.Hash); + } + + [TestMethod] + public void ByteArrays_V0() + { + var buffer = "1220a4edf38611d7d4a2d3ff2d97f88a7256eba31b57982f803b4de7bbeb0343c37b".ToHexBuffer(); + var cid = Cid.Read(buffer); + Assert.AreEqual(0, cid.Version); + Assert.AreEqual("dag-pb", cid.ContentType); + Assert.AreEqual("QmZSU1xNFsBtCnzK2Nk9N4bAxQiVNdmugU9DQDE3ntkTpe", cid.Hash.ToString()); + + var clone = cid.ToArray(); + CollectionAssert.AreEqual(buffer, clone); + } + } +} diff --git a/src/Lib.P2P.Tests/ConnectionManagerTest.cs b/src/Lib.P2P.Tests/ConnectionManagerTest.cs new file mode 100644 index 0000000000..6e971b3791 --- /dev/null +++ b/src/Lib.P2P.Tests/ConnectionManagerTest.cs @@ -0,0 +1,336 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests +{ + [TestClass] + public class ConnectionManagerTest + { + private MultiHash aId = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb"; + private MultiHash bId = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h"; + + [TestMethod] + public void IsConnected() + { + var manager = new ConnectionManager(); + var peer = new Peer {Id = aId}; + var connection = new PeerConnection {RemotePeer = peer, Stream = Stream.Null}; + + Assert.IsFalse(manager.IsConnected(peer)); + manager.Add(connection); + Assert.IsTrue(manager.IsConnected(peer)); + } + + [TestMethod] + public void IsConnected_NotActive() + { + var manager = new ConnectionManager(); + var peer = new Peer {Id = aId}; + var connection = new PeerConnection {RemotePeer = peer, Stream = Stream.Null}; + + Assert.IsFalse(manager.IsConnected(peer)); + + manager.Add(connection); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + + connection.Stream = null; + Assert.IsFalse(manager.IsConnected(peer)); + Assert.AreEqual(0, manager.Connections.Count()); + } + + [TestMethod] + public void Add_Duplicate() + { + var manager = new ConnectionManager(); + var peer = new Peer {Id = aId}; + var a = new PeerConnection {RemotePeer = peer, Stream = Stream.Null}; + var b = new PeerConnection {RemotePeer = peer, Stream = Stream.Null}; + + Assert.AreSame(a, manager.Add(a)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + + Assert.AreSame(b, manager.Add(b)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(2, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + Assert.IsNotNull(b.Stream); + + manager.Clear(); + Assert.AreEqual(0, manager.Connections.Count()); + Assert.IsNull(a.Stream); + Assert.IsNull(b.Stream); + } + + [TestMethod] + public void Add_Duplicate_SameConnection() + { + var manager = new ConnectionManager(); + var peer = new Peer {Id = aId}; + var a = new PeerConnection {RemotePeer = peer, Stream = Stream.Null}; + + Assert.AreSame(a, manager.Add(a)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + + Assert.AreSame(a, manager.Add(a)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + } + + [TestMethod] + public void Add_Duplicate_PeerConnectedAddress() + { + var address = "/ip6/::1/tcp/4007"; + + var manager = new ConnectionManager(); + var peer = new Peer {Id = aId, ConnectedAddress = address}; + var a = new PeerConnection {RemotePeer = peer, RemoteAddress = address, Stream = Stream.Null}; + var b = new PeerConnection {RemotePeer = peer, RemoteAddress = address, Stream = Stream.Null}; + + Assert.AreSame(a, manager.Add(a)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + Assert.AreEqual(address, peer.ConnectedAddress); + + Assert.AreSame(b, manager.Add(b)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(2, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + Assert.IsNotNull(b.Stream); + Assert.AreEqual(address, peer.ConnectedAddress); + } + + [TestMethod] + public void Maintains_PeerConnectedAddress() + { + var address1 = "/ip4/127.0.0.1/tcp/4007"; + var address2 = "/ip4/127.0.0.2/tcp/4007"; + + var manager = new ConnectionManager(); + var peer = new Peer {Id = aId}; + var a = new PeerConnection {RemotePeer = peer, RemoteAddress = address1, Stream = Stream.Null}; + var b = new PeerConnection {RemotePeer = peer, RemoteAddress = address2, Stream = Stream.Null}; + + Assert.AreSame(a, manager.Add(a)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + Assert.AreEqual(address1, peer.ConnectedAddress); + + Assert.AreSame(b, manager.Add(b)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(2, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + Assert.IsNotNull(b.Stream); + Assert.AreEqual(address1, peer.ConnectedAddress); + + Assert.IsTrue(manager.Remove(a)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNull(a.Stream); + Assert.IsNotNull(b.Stream); + Assert.AreEqual(address2, peer.ConnectedAddress); + + Assert.IsTrue(manager.Remove(b)); + Assert.IsFalse(manager.IsConnected(peer)); + Assert.AreEqual(0, manager.Connections.Count()); + Assert.IsNull(a.Stream); + Assert.IsNull(b.Stream); + Assert.IsNull(peer.ConnectedAddress); + } + + [TestMethod] + public void Add_Duplicate_ExistingIsDead() + { + var address = "/ip6/::1/tcp/4007"; + + var manager = new ConnectionManager(); + var peer = new Peer {Id = aId, ConnectedAddress = address}; + var a = new PeerConnection {RemotePeer = peer, RemoteAddress = address, Stream = Stream.Null}; + var b = new PeerConnection {RemotePeer = peer, RemoteAddress = address, Stream = Stream.Null}; + + Assert.AreSame(a, manager.Add(a)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + Assert.AreEqual(address, peer.ConnectedAddress); + + a.Stream = null; + Assert.AreSame(b, manager.Add(b)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNull(a.Stream); + Assert.IsNotNull(b.Stream); + Assert.AreEqual(address, peer.ConnectedAddress); + } + + [TestMethod] + public void Add_NotActive() + { + var manager = new ConnectionManager(); + var peer = new Peer {Id = aId}; + var a = new PeerConnection {RemotePeer = peer, Stream = Stream.Null}; + var b = new PeerConnection {RemotePeer = peer, Stream = Stream.Null}; + + Assert.AreSame(a, manager.Add(a)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + a.Stream = null; + + Assert.AreSame(b, manager.Add(b)); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNull(a.Stream); + Assert.IsNotNull(b.Stream); + + Assert.AreSame(b, manager.Connections.First()); + } + + [TestMethod] + public void Remove_Connection() + { + var manager = new ConnectionManager(); + var peer = new Peer {Id = aId}; + var a = new PeerConnection {RemotePeer = peer, Stream = Stream.Null}; + + manager.Add(a); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + + Assert.IsTrue(manager.Remove(a)); + Assert.IsFalse(manager.IsConnected(peer)); + Assert.AreEqual(0, manager.Connections.Count()); + Assert.IsNull(a.Stream); + } + + [TestMethod] + public void Remove_PeerId() + { + var manager = new ConnectionManager(); + var peer = new Peer {Id = aId}; + var a = new PeerConnection {RemotePeer = peer, Stream = Stream.Null}; + + manager.Add(a); + Assert.IsTrue(manager.IsConnected(peer)); + Assert.AreEqual(1, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + + Assert.IsTrue(manager.Remove(peer.Id)); + Assert.IsFalse(manager.IsConnected(peer)); + Assert.AreEqual(0, manager.Connections.Count()); + Assert.IsNull(a.Stream); + } + + [TestMethod] + public void Remove_DoesNotExist() + { + var manager = new ConnectionManager(); + var peer = new Peer {Id = aId}; + var a = new PeerConnection {RemotePeer = peer, Stream = Stream.Null}; + + Assert.IsFalse(manager.Remove(a)); + Assert.IsFalse(manager.IsConnected(peer)); + Assert.AreEqual(0, manager.Connections.Count()); + Assert.IsNull(a.Stream); + } + + [TestMethod] + public void Clear() + { + var manager = new ConnectionManager(); + var peerA = new Peer {Id = aId}; + var peerB = new Peer {Id = bId}; + var a = new PeerConnection {RemotePeer = peerA, Stream = Stream.Null}; + var b = new PeerConnection {RemotePeer = peerB, Stream = Stream.Null}; + + Assert.AreSame(a, manager.Add(a)); + Assert.AreSame(b, manager.Add(b)); + Assert.IsTrue(manager.IsConnected(peerA)); + Assert.IsTrue(manager.IsConnected(peerB)); + Assert.AreEqual(2, manager.Connections.Count()); + Assert.IsNotNull(a.Stream); + Assert.IsNotNull(b.Stream); + + manager.Clear(); + Assert.IsFalse(manager.IsConnected(peerA)); + Assert.IsFalse(manager.IsConnected(peerB)); + Assert.AreEqual(0, manager.Connections.Count()); + Assert.IsNull(a.Stream); + Assert.IsNull(b.Stream); + } + + [TestMethod] + public void PeerDisconnectedEvent_RemovingPeer() + { + var gotEvent = false; + var manager = new ConnectionManager(); + manager.PeerDisconnected += (s, e) => gotEvent = true; + var peerA = new Peer {Id = aId}; + var a = new PeerConnection {RemotePeer = peerA, Stream = Stream.Null}; + manager.Add(a); + + manager.Remove(peerA.Id); + Assert.IsTrue(gotEvent); + } + + [TestMethod] + public void PeerDisconnectedEvent_RemovingConnection() + { + var gotEvent = 0; + var manager = new ConnectionManager(); + manager.PeerDisconnected += (s, e) => gotEvent += 1; + var peerA = new Peer {Id = aId}; + var a = new PeerConnection {RemotePeer = peerA, Stream = Stream.Null}; + manager.Add(a); + + manager.Remove(a); + Assert.AreEqual(1, gotEvent); + } + + [TestMethod] + public void PeerDisconnectedEvent_ConnectionClose() + { + var gotEvent = 0; + var manager = new ConnectionManager(); + manager.PeerDisconnected += (s, e) => gotEvent += 1; + var peerA = new Peer {Id = aId}; + var a = new PeerConnection {RemotePeer = peerA, Stream = Stream.Null}; + manager.Add(a); + a.Dispose(); + Assert.AreEqual(1, gotEvent); + } + } +} diff --git a/src/Lib.P2P.Tests/Cryptography/CtrStreamCipherTest.cs b/src/Lib.P2P.Tests/Cryptography/CtrStreamCipherTest.cs new file mode 100644 index 0000000000..cfedfdc5c8 --- /dev/null +++ b/src/Lib.P2P.Tests/Cryptography/CtrStreamCipherTest.cs @@ -0,0 +1,101 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Text; +using Lib.P2P.Cryptography; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Lib.P2P.Tests.Cryptography +{ + [TestClass] + public sealed class CtrStreamCipherTest + { + [TestMethod] + public void SameAsNodeJs() + { + var key = new byte[32]; + var iv = new byte[16]; + var encrypt = new CtrStreamCipher(new AesEngine()); + var p = new ParametersWithIV(new KeyParameter(key), iv); + encrypt.Init(true, p); + + var plain = new[] {(byte) 'a'}; + var actual = new byte[plain.Length]; + + var expected = new byte[] {0xbd}; + encrypt.ProcessBytes(plain, 0, plain.Length, actual, 0); + Assert.AreEqual(expected[0], actual[0]); + + expected = new byte[] {0xf4}; + encrypt.ProcessBytes(plain, 0, plain.Length, actual, 0); + Assert.AreEqual(expected[0], actual[0]); + } + + [TestMethod] + public void MultiBlock() + { + var key = new byte[32]; + var iv = new byte[16]; + var encrypt = new CtrStreamCipher(new AesEngine()); + var p = new ParametersWithIV(new KeyParameter(key), iv); + encrypt.Init(true, p); + + var plain = Encoding.UTF8.GetBytes("this is some text that spans multiple blocks"); + var expected1 = "a8fda90b8229faa9de27cf71b2f045ff272ffe93a63116cad902da82e4a606e7bace305128400902682daea0" + .ToHexBuffer(); + var expected2 = "ce9bf46b520970ea44c94711f1d690f6012641e6bc3e915b3d2b8f0861852c483365469b2261a98deed81443" + .ToHexBuffer(); + var actual = new byte[plain.Length]; + + encrypt.ProcessBytes(plain, 0, plain.Length, actual, 0); + CollectionAssert.AreEqual(expected1, actual); + + encrypt.ProcessBytes(plain, 0, plain.Length, actual, 0); + CollectionAssert.AreEqual(expected2, actual); + } + + [TestMethod] + public void SingleBlock() + { + var key = new byte[32]; + var iv = new byte[16]; + var encrypt = new CtrStreamCipher(new AesEngine()); + var p = new ParametersWithIV(new KeyParameter(key), iv); + encrypt.Init(true, p); + + var plain = Encoding.UTF8.GetBytes("1234567890123456"); + var expected1 = "eda7f34c9776beb194789326a1b015b1".ToHexBuffer(); + var expected2 = "623db9cff2730181905385c3f7ff46bd".ToHexBuffer(); + var actual = new byte[plain.Length]; + + encrypt.ProcessBytes(plain, 0, plain.Length, actual, 0); + CollectionAssert.AreEqual(expected1, actual); + + encrypt.ProcessBytes(plain, 0, plain.Length, actual, 0); + CollectionAssert.AreEqual(expected2, actual); + } + } +} diff --git a/src/Lib.P2P.Tests/Cryptography/EphermalKeyTest.cs b/src/Lib.P2P.Tests/Cryptography/EphermalKeyTest.cs new file mode 100644 index 0000000000..8ce54d7ae8 --- /dev/null +++ b/src/Lib.P2P.Tests/Cryptography/EphermalKeyTest.cs @@ -0,0 +1,45 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Lib.P2P.Cryptography; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.Cryptography +{ + [TestClass] + public class EphermalKeyTest + { + [TestMethod] + public void SharedSecret() + { + var curve = "P-256"; + var alice = EphermalKey.Generate(curve); + var bob = EphermalKey.Generate(curve); + + var aliceSecret = alice.GenerateSharedSecret(bob); + var bobSecret = bob.GenerateSharedSecret(alice); + CollectionAssert.AreEqual(aliceSecret, bobSecret); + Assert.AreEqual(32, aliceSecret.Length); + } + } +} diff --git a/src/Lib.P2P.Tests/Cryptography/PreSharedKeyTest.cs b/src/Lib.P2P.Tests/Cryptography/PreSharedKeyTest.cs new file mode 100644 index 0000000000..c301372738 --- /dev/null +++ b/src/Lib.P2P.Tests/Cryptography/PreSharedKeyTest.cs @@ -0,0 +1,147 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using Lib.P2P.Cryptography; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests.Cryptography +{ + [TestClass] + public class PreSharedKeyTest + { + [TestMethod] + public void Defaults() + { + var psk = new PreSharedKey(); + Assert.IsNull(psk.Value); + Assert.AreEqual(0, psk.Length); + } + + [TestMethod] + public void LengthInBits() + { + var psk = new PreSharedKey {Value = new byte[] {1, 2}}; + Assert.AreEqual(16, psk.Length); + } + + [TestMethod] + public void Generate() + { + var psk = new PreSharedKey().Generate(); + Assert.IsNotNull(psk.Value); + Assert.AreEqual(256, psk.Length); + } + + [TestMethod] + public void Export_Base16() + { + var psk1 = new PreSharedKey().Generate(); + var s = new StringWriter(); + psk1.Export(s); + + var psk2 = new PreSharedKey(); + psk2.Import(new StringReader(s.ToString())); + CollectionAssert.AreEqual(psk1.Value, psk2.Value); + } + + [TestMethod] + public void Export_Base64() + { + var psk1 = new PreSharedKey().Generate(); + var s = new StringWriter(); + psk1.Export(s, "base64"); + + var psk2 = new PreSharedKey(); + psk2.Import(new StringReader(s.ToString())); + CollectionAssert.AreEqual(psk1.Value, psk2.Value); + } + + [TestMethod] + public void Export_Base16_is_default() + { + var psk = new PreSharedKey().Generate(); + var s1 = new StringWriter(); + var s2 = new StringWriter(); + psk.Export(s1); + psk.Export(s2); + Assert.AreEqual(s1.ToString(), s2.ToString()); + } + + [TestMethod] + [ExpectedException(typeof(Exception))] + public void Export_BadBase() + { + var psk = new PreSharedKey().Generate(); + var s = new StringWriter(); + psk.Export(s, "bad"); + } + + [TestMethod] + [ExpectedException(typeof(FormatException))] + public void Import_BadCodec() + { + var s = new StringReader("/bad/codec/"); + new PreSharedKey().Import(s); + } + + [TestMethod] + [ExpectedException(typeof(FormatException))] + public void Import_BadBase() + { + var s = new StringReader("/key/swarm/psk/1.0.0/\n/base128/"); + new PreSharedKey().Import(s); + } + + /// + /// A key generated with + /// > npm install ipfs-swarm-key-gen -g + /// > node-ipfs-swarm-key-gen + /// + [TestMethod] + public void Import_JS_Generated() + { + var key = "/key/swarm/psk/1.0.0/\n" + + "/base16/\n" + + "e8d6d31e8e02000010d7d31e8e020000f0d1fc609300000078f0d31e8e020000"; + var psk2 = new PreSharedKey(); + psk2.Import(new StringReader(key)); + + var expected = "e8d6d31e8e02000010d7d31e8e020000f0d1fc609300000078f0d31e8e020000".ToHexBuffer(); + CollectionAssert.AreEqual(expected, psk2.Value); + } + + [TestMethod] + public void Fingerprint() + { + var key = new PreSharedKey + { + Value = "e8d6d31e8e02000010d7d31e8e020000f0d1fc609300000078f0d31e8e020000".ToHexBuffer() + }; + var expected = "56a19299c05df1f2bb0e1d466002b6d9"; + Assert.AreEqual(expected, key.Fingerprint().ToHexString()); + } + } +} diff --git a/src/Lib.P2P.Tests/Cryptography/StretchedKeyTest.cs b/src/Lib.P2P.Tests/Cryptography/StretchedKeyTest.cs new file mode 100644 index 0000000000..7168396fb2 --- /dev/null +++ b/src/Lib.P2P.Tests/Cryptography/StretchedKeyTest.cs @@ -0,0 +1,72 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Lib.P2P.Cryptography; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.Cryptography +{ + [TestClass] + public class StretchedKeyTest + { + // from https://github.com/libp2p/js-libp2p-crypto/blob/ad478454d86787fffed30730605d6c76a36b4d61/test/fixtures/go-stretch-key.js + [TestMethod] + public void StretchedSecret() + { + var cipher = "AES-256"; + var hash = "SHA256"; + var secret = new byte[] + { + 195, 191, 209, 165, 209, 201, 127, 122, 136, 111, 31, 66, 111, 68, 38, 155, 216, 204, 46, 181, 200, 188, + 170, 204, 104, 74, 239, 251, 173, 114, 222, 234 + }; + StretchedKey.Generate(cipher, hash, secret, out var k1, out var k2); + + Assert.IsNotNull(k1); + CollectionAssert.AreEqual( + new byte[] {208, 132, 203, 169, 253, 52, 40, 83, 161, 91, 17, 71, 33, 136, 67, 96}, k1.Iv); + CollectionAssert.AreEqual( + new byte[] + { + 156, 48, 241, 157, 92, 248, 153, 186, 114, 127, 195, 114, 106, 104, 215, 133, 35, 11, 131, 137, 123, + 70, 74, 26, 15, 60, 189, 32, 67, 221, 115, 137 + }, k1.CipherKey); + CollectionAssert.AreEqual( + new byte[] {6, 179, 91, 245, 224, 56, 153, 120, 77, 140, 29, 5, 15, 213, 187, 65, 137, 230, 202, 120}, + k1.MacKey); + + Assert.IsNotNull(k2); + CollectionAssert.AreEqual( + new byte[] {236, 17, 34, 141, 90, 106, 197, 56, 197, 184, 157, 135, 91, 88, 112, 19}, k2.Iv); + CollectionAssert.AreEqual( + new byte[] + { + 151, 145, 195, 219, 76, 195, 102, 109, 187, 231, 100, 150, 132, 245, 251, 130, 254, 37, 178, 55, + 227, 34, 114, 39, 238, 34, 2, 193, 107, 130, 32, 87 + }, k2.CipherKey); + CollectionAssert.AreEqual( + new byte[] {3, 229, 77, 212, 241, 217, 23, 113, 220, 126, 38, 255, 18, 117, 108, 205, 198, 89, 1, 236}, + k2.MacKey); + } + } +} diff --git a/src/Lib.P2P.Tests/Discovery/BootstrapTest.cs b/src/Lib.P2P.Tests/Discovery/BootstrapTest.cs new file mode 100644 index 0000000000..634790cfb2 --- /dev/null +++ b/src/Lib.P2P.Tests/Discovery/BootstrapTest.cs @@ -0,0 +1,138 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Threading.Tasks; +using Lib.P2P.Discovery; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests.Discovery +{ + [TestClass] + public class BootstrapTest + { + [TestMethod] + public async Task NullList() + { + var bootstrap = new Bootstrap {Addresses = null}; + var found = 0; + bootstrap.PeerDiscovered += (s, e) => { ++found; }; + await bootstrap.StartAsync(); + Assert.AreEqual(0, found); + } + + [TestMethod] + public async Task Discovered() + { + var bootstrap = new Bootstrap + { + Addresses = new MultiAddress[] + { + "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + "/ip4/104.131.131.83/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" + } + }; + var found = 0; + bootstrap.PeerDiscovered += (s, peer) => + { + Assert.IsNotNull(peer); + Assert.AreEqual("QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", peer.Id.ToBase58()); + CollectionAssert.AreEqual(bootstrap.Addresses.ToArray(), peer.Addresses.ToArray()); + ++found; + }; + await bootstrap.StartAsync(); + Assert.AreEqual(1, found); + } + + [TestMethod] + public async Task Discovered_Multiple_Peers() + { + var bootstrap = new Bootstrap + { + Addresses = new MultiAddress[] + { + "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + "/ip4/127.0.0.1/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + "/ip4/104.131.131.83/tcp/4001/p2p/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + "/ip6/::/tcp/4001/p2p/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h" + } + }; + var found = 0; + bootstrap.PeerDiscovered += (s, peer) => + { + Assert.IsNotNull(peer); + ++found; + }; + await bootstrap.StartAsync(); + Assert.AreEqual(2, found); + } + + [TestMethod] + public async Task Stop_Removes_EventHandlers() + { + var bootstrap = new Bootstrap + { + Addresses = new MultiAddress[] + { + "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" + } + }; + var found = 0; + bootstrap.PeerDiscovered += (s, e) => + { + Assert.IsNotNull(e); + ++found; + }; + await bootstrap.StartAsync(); + Assert.AreEqual(1, found); + await bootstrap.StopAsync(); + + await bootstrap.StartAsync(); + Assert.AreEqual(1, found); + } + + [TestMethod] + public async Task Missing_ID_Is_Ignored() + { + var bootstrap = new Bootstrap + { + Addresses = new MultiAddress[] + { + "/ip4/104.131.131.82/tcp/4002", + "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" + } + }; + var found = 0; + bootstrap.PeerDiscovered += (s, e) => + { + Assert.IsNotNull(e); + Assert.IsNotNull(e.Addresses); + Assert.AreEqual(bootstrap.Addresses.Last(), e.Addresses.First()); + ++found; + }; + await bootstrap.StartAsync(); + Assert.AreEqual(1, found); + } + } +} diff --git a/src/Lib.P2P.Tests/Discovery/MdnsTest.cs b/src/Lib.P2P.Tests/Discovery/MdnsTest.cs new file mode 100644 index 0000000000..d94e3fbbb2 --- /dev/null +++ b/src/Lib.P2P.Tests/Discovery/MdnsTest.cs @@ -0,0 +1,156 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Discovery; +using Makaretu.Dns; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests.Discovery +{ + [TestClass] + public class MdnsTest + { + //Ignore test, contains race condition because of timing. + [Ignore] + [TestMethod] + public async Task DiscoveryNext() + { + var serviceName = $"_{Guid.NewGuid()}._udp"; + var peer1 = new Peer + { + Id = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + Addresses = new MultiAddress[] + {"/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"} + }; + var peer2 = new Peer + { + Id = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuK", + Addresses = new MultiAddress[] + {"/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuK"} + }; + var done = new ManualResetEvent(false); + var mdns1 = new MdnsNext + { + MulticastService = new MulticastService(), + ServiceName = serviceName, + LocalPeer = peer1 + }; + var mdns2 = new MdnsNext + { + MulticastService = new MulticastService(), + ServiceName = serviceName, + LocalPeer = peer2 + }; + mdns1.PeerDiscovered += (s, e) => + { + if (e.Id == peer2.Id) + done.Set(); + }; + await mdns1.StartAsync(); + mdns1.MulticastService.Start(); + await mdns2.StartAsync(); + mdns2.MulticastService.Start(); + try + { + Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(2)), "timeout"); + } + finally + { + await mdns1.StopAsync(); + await mdns2.StopAsync(); + mdns1.MulticastService.Stop(); + mdns2.MulticastService.Stop(); + } + } + + //Ignore test, contains race condition because of timing. + [Ignore] + [TestMethod] + public async Task DiscoveryJs() + { + var serviceName = $"_{Guid.NewGuid()}._udp"; + serviceName = "_foo._udp"; + var peer1 = new Peer + { + Id = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + Addresses = new MultiAddress[] + {"/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"} + }; + var peer2 = new Peer + { + Id = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuK", + Addresses = new MultiAddress[] + {"/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuK"} + }; + var done = new ManualResetEvent(false); + var mdns1 = new MdnsJs + { + MulticastService = new MulticastService(), + ServiceName = serviceName, + LocalPeer = peer1 + }; + var mdns2 = new MdnsJs + { + MulticastService = new MulticastService(), + ServiceName = serviceName, + LocalPeer = peer2 + }; + mdns1.PeerDiscovered += (s, e) => + { + if (e.Id == peer2.Id) + done.Set(); + }; + await mdns1.StartAsync(); + mdns1.MulticastService.Start(); + await mdns2.StartAsync(); + mdns2.MulticastService.Start(); + try + { + Assert.IsTrue(done.WaitOne(TimeSpan.FromSeconds(2)), "timeout"); + } + finally + { + await mdns1.StopAsync(); + await mdns2.StopAsync(); + mdns1.MulticastService.Stop(); + mdns2.MulticastService.Stop(); + } + } + + [TestMethod] + public void SafeDnsLabel() + { + Assert.AreEqual("a", MdnsNext.SafeLabel("a", 2)); + Assert.AreEqual("ab", MdnsNext.SafeLabel("ab", 2)); + Assert.AreEqual("ab.c", MdnsNext.SafeLabel("abc", 2)); + Assert.AreEqual("ab.cd", MdnsNext.SafeLabel("abcd", 2)); + Assert.AreEqual("ab.cd.e", MdnsNext.SafeLabel("abcde", 2)); + Assert.AreEqual("ab.cd.ef", MdnsNext.SafeLabel("abcdef", 2)); + Assert.AreEqual("ab.cd.ef.g", MdnsNext.SafeLabel("abcdefg", 2)); + } + } +} diff --git a/src/Lib.P2P.Tests/ExceptionAssert.cs b/src/Lib.P2P.Tests/ExceptionAssert.cs new file mode 100644 index 0000000000..23f73f2f6d --- /dev/null +++ b/src/Lib.P2P.Tests/ExceptionAssert.cs @@ -0,0 +1,71 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests +{ + /// + /// Asserting an . + /// + public static class ExceptionAssert + { + public static T Throws(Action action, string expectedMessage = null) where T : Exception + { + try + { + action(); + } + catch (AggregateException e) + { + var match = e.InnerExceptions.OfType().FirstOrDefault(); + if (match != null) + { + if (expectedMessage != null) + Assert.AreEqual(expectedMessage, match.Message, "Wrong exception message."); + return match; + } + + throw; + } + catch (T e) + { + if (expectedMessage != null) + Assert.AreEqual(expectedMessage, e.Message); + return e; + } + + Assert.Fail("Exception of type {0} should be thrown.", typeof(T)); + + // The compiler doesn't know that Assert.Fail will always throw an exception + return null; + } + + public static Exception Throws(Action action, string expectedMessage = null) + { + return Throws(action, expectedMessage); + } + } +} diff --git a/src/Lib.P2P.Tests/Lib.P2P.Tests.csproj b/src/Lib.P2P.Tests/Lib.P2P.Tests.csproj new file mode 100644 index 0000000000..b878223b12 --- /dev/null +++ b/src/Lib.P2P.Tests/Lib.P2P.Tests.csproj @@ -0,0 +1,20 @@ + + + net6.0 + Lib.P2P.Tests + Darren Priestnall (darren.op@catalystnet.org) + true + Lib.P2P.Tests.snk + true + false + + + + + + + + + + + diff --git a/src/Lib.P2P.Tests/Lib.P2P.Tests.snk b/src/Lib.P2P.Tests/Lib.P2P.Tests.snk new file mode 100644 index 0000000000..e364356b76 Binary files /dev/null and b/src/Lib.P2P.Tests/Lib.P2P.Tests.snk differ diff --git a/src/Lib.P2P.Tests/MessageTrackerTest.cs b/src/Lib.P2P.Tests/MessageTrackerTest.cs new file mode 100644 index 0000000000..7f65aee18d --- /dev/null +++ b/src/Lib.P2P.Tests/MessageTrackerTest.cs @@ -0,0 +1,42 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests +{ + [TestClass] + public class MessageTrackerTest + { + [TestMethod] + public void Tracking() + { + var tracker = new MessageTracker(); + var now = DateTime.Now; + Assert.IsFalse(tracker.RecentlySeen("a", now)); + Assert.IsTrue(tracker.RecentlySeen("a", now)); + Assert.IsFalse(tracker.RecentlySeen("a", now + tracker.Recent)); + } + } +} diff --git a/src/Lib.P2P.Tests/MultiAddressExtensionsTest.cs b/src/Lib.P2P.Tests/MultiAddressExtensionsTest.cs new file mode 100644 index 0000000000..14d08a23a6 --- /dev/null +++ b/src/Lib.P2P.Tests/MultiAddressExtensionsTest.cs @@ -0,0 +1,94 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Net.Sockets; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests +{ + [TestClass] + public class MultiAddressExtensionsTest + { + [TestMethod] + public void Cloning() + { + var a = new MultiAddress("/dns/libp2p.io/tcp/5001"); + var b = a.Clone(); + Assert.AreEqual(a, b); + Assert.AreNotSame(a.Protocols, b.Protocols); + } + + [TestMethod] + public async Task Resolving() + { + var local = new MultiAddress("/ip4/127.0.0.1/tcp/5001"); + var r0 = await local.ResolveAsync(); + Assert.AreEqual(1, r0.Count); + Assert.AreEqual(local, r0[0]); + } + + [TestMethod] + public async Task Resolving_Dns() + { + var dns = await new MultiAddress("/dns/libp2p.io/tcp/5001").ResolveAsync(); + Assert.AreNotEqual(0, dns.Count); + var dns4 = await new MultiAddress("/dns4/libp2p.io/tcp/5001").ResolveAsync(); + var dns6 = await new MultiAddress("/dns6/libp2p.io/tcp/5001").ResolveAsync(); + Assert.AreEqual(dns.Count, dns4.Count + dns6.Count); + } + + [TestMethod] + public async Task Resolving_HTTP() + { + var r = await new MultiAddress("/ip4/127.0.0.1/http").ResolveAsync(); + Assert.AreEqual("/ip4/127.0.0.1/http/tcp/80", r.First()); + + r = await new MultiAddress("/ip4/127.0.0.1/http/tcp/8080").ResolveAsync(); + Assert.AreEqual("/ip4/127.0.0.1/http/tcp/8080", r.First()); + } + + [TestMethod] + public async Task Resolving_HTTPS() + { + var r = await new MultiAddress("/ip4/127.0.0.1/https").ResolveAsync(); + Assert.AreEqual("/ip4/127.0.0.1/https/tcp/443", r.First()); + + r = await new MultiAddress("/ip4/127.0.0.1/https/tcp/4433").ResolveAsync(); + Assert.AreEqual("/ip4/127.0.0.1/https/tcp/4433", r.First()); + } + + [TestMethod] + public void Resolving_Unknown() + { + ExceptionAssert.Throws(() => + { + var _ = new MultiAddress("/dns/does.not.exist/tcp/5001") + .ResolveAsync() + .Result; + }); + } + } +} diff --git a/src/Lib.P2P.Tests/MultiAdressBlackListTest.cs b/src/Lib.P2P.Tests/MultiAdressBlackListTest.cs new file mode 100644 index 0000000000..d561fa04d9 --- /dev/null +++ b/src/Lib.P2P.Tests/MultiAdressBlackListTest.cs @@ -0,0 +1,114 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests +{ + [TestClass] + public sealed class MultiAddressBlackListTest + { + private MultiAddress a = "/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + private MultiAddress a1 = "/ip4/127.0.0.1/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + private MultiAddress b = "/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + private MultiAddress c = "/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64"; + private MultiAddress d = "/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64"; + + [TestMethod] + public void Allowed() + { + var policy = new MultiAddressBlackList {a, b}; + Assert.IsFalse(policy.IsAllowed(a)); + Assert.IsFalse(policy.IsAllowed(a1)); + Assert.IsFalse(policy.IsAllowed(b)); + Assert.IsTrue(policy.IsAllowed(c)); + Assert.IsTrue(policy.IsAllowed(d)); + } + + [TestMethod] + public void Allowed_Alias() + { + var policy = new MultiAddressBlackList {a}; + Assert.IsFalse(policy.IsAllowed(a)); + Assert.IsFalse(policy.IsAllowed(a1)); + Assert.IsFalse(policy.IsAllowed(b)); + Assert.IsTrue(policy.IsAllowed(c)); + Assert.IsTrue(policy.IsAllowed(d)); + } + + [TestMethod] + public void Empty() + { + var policy = new MultiAddressBlackList(); + Assert.IsTrue(policy.IsAllowed(a)); + } + + [TestMethod] + public void Collection() + { + MultiAddress addressA = "/ip4/127.0.0.1"; + MultiAddress addressB = "/ip4/127.0.0.2"; + + var policy = new MultiAddressBlackList(); + Assert.IsFalse(policy.IsReadOnly); + Assert.AreEqual(0, policy.Count); + Assert.IsFalse(policy.Contains(addressA)); + Assert.IsFalse(policy.Contains(addressB)); + + policy.Add(addressA); + Assert.AreEqual(1, policy.Count); + Assert.IsTrue(policy.Contains(addressA)); + Assert.IsFalse(policy.Contains(addressB)); + + policy.Add(addressA); + Assert.AreEqual(1, policy.Count); + Assert.IsTrue(policy.Contains(addressA)); + Assert.IsFalse(policy.Contains(addressB)); + + policy.Add(addressB); + Assert.AreEqual(2, policy.Count); + Assert.IsTrue(policy.Contains(addressA)); + Assert.IsTrue(policy.Contains(addressB)); + + policy.Remove(addressB); + Assert.AreEqual(1, policy.Count); + Assert.IsTrue(policy.Contains(addressA)); + Assert.IsFalse(policy.Contains(addressB)); + + var array = new MultiAddress[1]; + policy.CopyTo(array, 0); + Assert.AreSame(addressA, array[0]); + + foreach (var filter in policy) + { + Assert.AreSame(addressA, filter); + } + + policy.Clear(); + Assert.AreEqual(0, policy.Count); + Assert.IsFalse(policy.Contains(addressA)); + Assert.IsFalse(policy.Contains(addressB)); + } + } +} diff --git a/src/Lib.P2P.Tests/MultiAdressWhiteListTest.cs b/src/Lib.P2P.Tests/MultiAdressWhiteListTest.cs new file mode 100644 index 0000000000..b0b6cd2c9b --- /dev/null +++ b/src/Lib.P2P.Tests/MultiAdressWhiteListTest.cs @@ -0,0 +1,114 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests +{ + [TestClass] + public sealed class MultiAddressWhiteListTest + { + private readonly MultiAddress _a = "/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + private readonly MultiAddress _a1 = "/ip4/127.0.0.1/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + private readonly MultiAddress _b = "/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + private readonly MultiAddress _c = "/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64"; + private readonly MultiAddress _d = "/p2p/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64"; + + [TestMethod] + public void Allowed() + { + var policy = new MultiAddressWhiteList {_a, _b}; + Assert.IsTrue(policy.IsAllowed(_a)); + Assert.IsTrue(policy.IsAllowed(_a1)); + Assert.IsTrue(policy.IsAllowed(_b)); + Assert.IsFalse(policy.IsAllowed(_c)); + Assert.IsFalse(policy.IsAllowed(_d)); + } + + [TestMethod] + public void Allowed_Alias() + { + var policy = new MultiAddressWhiteList {_a}; + Assert.IsTrue(policy.IsAllowed(_a)); + Assert.IsTrue(policy.IsAllowed(_a1)); + Assert.IsTrue(policy.IsAllowed(_b)); + Assert.IsFalse(policy.IsAllowed(_c)); + Assert.IsFalse(policy.IsAllowed(_d)); + } + + [TestMethod] + public void Empty() + { + var policy = new MultiAddressWhiteList(); + Assert.IsTrue(policy.IsAllowed(_a)); + } + + [TestMethod] + public void Collection() + { + MultiAddress addressA = "/ip4/127.0.0.1"; + MultiAddress addressB = "/ip4/127.0.0.2"; + + var policy = new MultiAddressWhiteList(); + Assert.IsFalse(policy.IsReadOnly); + Assert.AreEqual(0, policy.Count); + Assert.IsFalse(policy.Contains(addressA)); + Assert.IsFalse(policy.Contains(addressB)); + + policy.Add(addressA); + Assert.AreEqual(1, policy.Count); + Assert.IsTrue(policy.Contains(addressA)); + Assert.IsFalse(policy.Contains(addressB)); + + policy.Add(addressA); + Assert.AreEqual(1, policy.Count); + Assert.IsTrue(policy.Contains(addressA)); + Assert.IsFalse(policy.Contains(addressB)); + + policy.Add(addressB); + Assert.AreEqual(2, policy.Count); + Assert.IsTrue(policy.Contains(addressA)); + Assert.IsTrue(policy.Contains(addressB)); + + policy.Remove(addressB); + Assert.AreEqual(1, policy.Count); + Assert.IsTrue(policy.Contains(addressA)); + Assert.IsFalse(policy.Contains(addressB)); + + var array = new MultiAddress[1]; + policy.CopyTo(array, 0); + Assert.AreSame(addressA, array[0]); + + foreach (var filter in policy) + { + Assert.AreSame(addressA, filter); + } + + policy.Clear(); + Assert.AreEqual(0, policy.Count); + Assert.IsFalse(policy.Contains(addressA)); + Assert.IsFalse(policy.Contains(addressB)); + } + } +} diff --git a/src/Lib.P2P.Tests/Multiplex/HeaderTest.cs b/src/Lib.P2P.Tests/Multiplex/HeaderTest.cs new file mode 100644 index 0000000000..d560446bf4 --- /dev/null +++ b/src/Lib.P2P.Tests/Multiplex/HeaderTest.cs @@ -0,0 +1,55 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using Lib.P2P.Multiplex; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.Multiplex +{ + [TestClass] + public class HeaderTest + { + [TestMethod] + public void StreamIds() + { + Roundtrip(0, PacketType.NewStream); + Roundtrip(1, PacketType.NewStream); + Roundtrip(0x1234, PacketType.NewStream); + Roundtrip(0x12345678, PacketType.NewStream); + Roundtrip(Header.MinStreamId, PacketType.NewStream); + Roundtrip(Header.MaxStreamId, PacketType.NewStream); + } + + private void Roundtrip(long id, PacketType type) + { + var header1 = new Header {StreamId = id, PacketType = type}; + var ms = new MemoryStream(); + header1.WriteAsync(ms).Wait(); + ms.Position = 0; + var header2 = Header.ReadAsync(ms).Result; + Assert.AreEqual(header1.StreamId, header2.StreamId); + Assert.AreEqual(header1.PacketType, header2.PacketType); + } + } +} diff --git a/src/Lib.P2P.Tests/Multiplex/MuxerTest.cs b/src/Lib.P2P.Tests/Multiplex/MuxerTest.cs new file mode 100644 index 0000000000..b48d211e57 --- /dev/null +++ b/src/Lib.P2P.Tests/Multiplex/MuxerTest.cs @@ -0,0 +1,189 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Multiplex; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests.Multiplex +{ + [TestClass] + public class MuxerTest + { + [TestMethod] + public void Defaults() + { + var muxer = new Muxer(); + Assert.AreEqual(true, muxer.Initiator); + Assert.AreEqual(false, muxer.Receiver); + } + + [TestMethod] + public void InitiatorReceiver() + { + var muxer = new Muxer {Initiator = true}; + Assert.AreEqual(true, muxer.Initiator); + Assert.AreEqual(false, muxer.Receiver); + Assert.AreEqual(0, muxer.NextStreamId & 1); + + muxer.Receiver = true; + Assert.AreEqual(false, muxer.Initiator); + Assert.AreEqual(true, muxer.Receiver); + Assert.AreEqual(1, muxer.NextStreamId & 1); + } + + [TestMethod] + public async Task NewStream_Send() + { + var channel = new MemoryStream(); + var muxer = new Muxer {Channel = channel, Initiator = true}; + var nextId = muxer.NextStreamId; + var stream = await muxer.CreateStreamAsync("foo"); + + // Correct stream id is assigned. + Assert.AreEqual(nextId, stream.Id); + Assert.AreEqual(nextId + 2, muxer.NextStreamId); + Assert.AreEqual("foo", stream.Name); + + // Substreams are managed. + Assert.AreEqual(1, muxer.Substreams.Count); + Assert.AreSame(stream, muxer.Substreams[stream.Id]); + + // NewStream message is sent. + channel.Position = 0; + Assert.AreEqual(stream.Id << 3, channel.ReadVarint32()); + Assert.AreEqual(3, channel.ReadVarint32()); + var name = new byte[3]; + channel.Read(name, 0, 3); + Assert.AreEqual("foo", Encoding.UTF8.GetString(name)); + Assert.AreEqual(channel.Length, channel.Position); + } + + [TestMethod] + public async Task NewStream_Receive() + { + var channel = new MemoryStream(); + var muxer1 = new Muxer {Channel = channel, Initiator = true}; + await muxer1.CreateStreamAsync("foo"); + await muxer1.CreateStreamAsync("bar"); + + channel.Position = 0; + var muxer2 = new Muxer {Channel = channel}; + var n = 0; + muxer2.SubstreamCreated += (s, e) => ++n; + await muxer2.ProcessRequestsAsync(); + Assert.AreEqual(2, n); + } + + [TestMethod] + public async Task NewStream_AlreadyAssigned() + { + var channel = new MemoryStream(); + var muxer1 = new Muxer {Channel = channel, Initiator = true}; + await muxer1.CreateStreamAsync("foo"); + var muxer2 = new Muxer {Channel = channel, Initiator = true}; + await muxer2.CreateStreamAsync("bar"); + + channel.Position = 0; + var muxer3 = new Muxer {Channel = channel}; + await muxer3.ProcessRequestsAsync(new CancellationTokenSource(500).Token); + + // The channel is closed because of 2 new streams with same id. + Assert.IsFalse(channel.CanRead); + Assert.IsFalse(channel.CanWrite); + } + + [TestMethod] + public async Task NewStream_Event() + { + var channel = new MemoryStream(); + var muxer1 = new Muxer {Channel = channel, Initiator = true}; + await muxer1.CreateStreamAsync("foo"); + await muxer1.CreateStreamAsync("bar"); + + channel.Position = 0; + var muxer2 = new Muxer {Channel = channel}; + var createCount = 0; + muxer2.SubstreamCreated += (s, e) => { ++createCount; }; + await muxer2.ProcessRequestsAsync(); + Assert.AreEqual(2, createCount); + } + + [TestMethod] + public async Task CloseStream_Event() + { + var channel = new MemoryStream(); + var muxer1 = new Muxer {Channel = channel, Initiator = true}; + await using (await muxer1.CreateStreamAsync("foo")) + { + await using (await muxer1.CreateStreamAsync("bar")) + { + // open and close a stream. + } + } + + channel.Position = 0; + var muxer2 = new Muxer {Channel = channel}; + var closeCount = 0; + muxer2.SubstreamClosed += (s, e) => { ++closeCount; }; + await muxer2.ProcessRequestsAsync(); + Assert.AreEqual(2, closeCount); + } + + [TestMethod] + public async Task AcquireWrite() + { + var muxer = new Muxer(); + var tasks = new List> + { + Task.Run(async () => + { + using (await muxer.AcquireWriteAccessAsync()) + { + await Task.Delay(100); + } + + return "step 1"; + }), + Task.Run(async () => + { + using (await muxer.AcquireWriteAccessAsync()) + { + await Task.Delay(50); + } + + return "step 2"; + }), + }; + + var done = await Task.WhenAll(tasks); + Assert.AreEqual("step 1", done[0]); + Assert.AreEqual("step 2", done[1]); + } + } +} diff --git a/src/Lib.P2P.Tests/Multiplex/SubstreamTest.cs b/src/Lib.P2P.Tests/Multiplex/SubstreamTest.cs new file mode 100644 index 0000000000..ba0aee27b6 --- /dev/null +++ b/src/Lib.P2P.Tests/Multiplex/SubstreamTest.cs @@ -0,0 +1,195 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading.Tasks; +using Lib.P2P.Multiplex; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests.Multiplex +{ + [TestClass] + public class SubstreamTest + { + [TestMethod] + public void Seeking() + { + var stream = new Substream(); + Assert.IsFalse(stream.CanSeek); + ExceptionAssert.Throws(() => { stream.Seek(0, SeekOrigin.Begin); }); + ExceptionAssert.Throws(() => { stream.Position = 0; }); + ExceptionAssert.Throws(() => + { + var _ = stream.Position; + }); + } + + [TestMethod] + public void Timeout() + { + var stream = new Substream(); + Assert.IsFalse(stream.CanTimeout); + ExceptionAssert.Throws(() => { stream.ReadTimeout = 0; }); + ExceptionAssert.Throws(() => + { + var _ = stream.ReadTimeout; + }); + ExceptionAssert.Throws(() => { stream.WriteTimeout = 0; }); + ExceptionAssert.Throws(() => + { + var _ = stream.WriteTimeout; + }); + } + + [TestMethod] + public void Length() + { + var stream = new Substream(); + ExceptionAssert.Throws(() => { stream.SetLength(0); }); + ExceptionAssert.Throws(() => + { + var _ = stream.Length; + }); + } + + [TestMethod] + public async Task Reading() + { + var m1 = new byte[] {1, 2, 3, 4}; + var m2 = new byte[m1.Length]; + var stream = new Substream(); + stream.AddData(new byte[] {1, 2}); + stream.AddData(new byte[] {3, 4}); + stream.NoMoreData(); + Assert.IsTrue(stream.CanRead); + + m2[0] = (byte) stream.ReadByte(); + Assert.AreEqual(1, stream.Read(m2, 1, 1)); + Assert.AreEqual(2, await stream.ReadAsync(m2, 2, 2)); + CollectionAssert.AreEqual(m1, m2); + + Assert.AreEqual(-1, stream.ReadByte()); + Assert.IsFalse(stream.CanRead); + } + + [TestMethod] + public async Task Reading_Partial() + { + var m1 = new byte[] {1, 2, 3, 4}; + var m2 = new byte[m1.Length]; + var stream = new Substream(); + stream.AddData(m1); + stream.NoMoreData(); + + Assert.AreEqual(4, await stream.ReadAsync(m2, 0, 5)); + CollectionAssert.AreEqual(m1, m2); + + Assert.AreEqual(-1, stream.ReadByte()); + Assert.IsFalse(stream.CanRead); + } + + [TestMethod] + public async Task Reading_Delayed_Partial() + { + var m1 = new byte[] {1, 2, 3, 4}; + var m2 = new byte[m1.Length]; + var stream = new Substream(); +#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + Task.Run(async () => + { + await Task.Delay(100); + stream.AddData(new byte[] {1, 2}); + await Task.Delay(100); + stream.AddData(new byte[] {3, 4}); + await Task.Delay(100); + stream.NoMoreData(); + }); +#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed + + Assert.AreEqual(4, await stream.ReadAsync(m2, 0, 5)); + CollectionAssert.AreEqual(m1, m2); + } + + [TestMethod] + public void Reading_Empty() + { + var stream = new Substream(); + var _ = Task.Run(async () => + { + await Task.Delay(100); + stream.NoMoreData(); + }); + + Assert.AreEqual(-1, stream.ReadByte()); + } + + [TestMethod] + public async Task Reading_ClosedStream() + { + var m1 = new byte[10]; + var stream = new Substream(); + stream.NoMoreData(); + Assert.AreEqual(0, await stream.ReadAsync(m1, 0, 10)); + } + + [TestMethod] + public async Task Writing() + { + var ms = new MemoryStream(); + var muxer = new Muxer {Channel = ms}; + var stream = new Substream {Muxer = muxer}; + var m1 = new byte[1]; + stream.AddData(new byte[] {10}); + Assert.IsTrue(stream.CanRead); + Assert.IsTrue(stream.CanWrite); + + Assert.AreEqual(1, await stream.ReadAsync(m1, 0, 1)); + await stream.WriteAsync(m1, 0, 1); + stream.WriteByte(11); + await stream.FlushAsync(); + + ms.Position = 0; + var header = await Header.ReadAsync(ms); + var length = await Varint.ReadVarint32Async(ms); + var payload = new byte[length]; + ms.Read(payload, 0, length); + Assert.AreEqual(stream.Id, header.StreamId); + Assert.AreEqual(2, payload.Length); + CollectionAssert.AreEqual(new byte[] {10, 11}, payload); + } + + [TestMethod] + public void Disposable() + { + var s = new Substream(); + Assert.IsTrue(s.CanRead); + Assert.IsTrue(s.CanWrite); + + s.Dispose(); + Assert.IsFalse(s.CanRead); + Assert.IsFalse(s.CanWrite); + } + } +} diff --git a/src/Lib.P2P.Tests/PeerConnectionTest.cs b/src/Lib.P2P.Tests/PeerConnectionTest.cs new file mode 100644 index 0000000000..36a5a82ca9 --- /dev/null +++ b/src/Lib.P2P.Tests/PeerConnectionTest.cs @@ -0,0 +1,102 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using Lib.P2P.Protocols; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests +{ + [TestClass] + public class PeerConnectionTest + { + [TestMethod] + public void Disposing() + { + var closeCount = 0; + var stream = new MemoryStream(); + var connection = new PeerConnection {Stream = stream}; + connection.Closed += (s, e) => { ++closeCount; }; + Assert.IsTrue(connection.IsActive); + Assert.IsNotNull(connection.Stream); + + connection.Dispose(); + Assert.IsFalse(connection.IsActive); + Assert.IsNull(connection.Stream); + + // Can be disposed multiple times. + connection.Dispose(); + + Assert.IsFalse(connection.IsActive); + Assert.AreEqual(1, closeCount); + } + + [TestMethod] + public void Stats() + { + var stream = new MemoryStream(); + var connection = new PeerConnection {Stream = stream}; + Assert.AreEqual(0, connection.BytesRead); + Assert.AreEqual(0, connection.BytesWritten); + + var buffer = new byte[] {1, 2, 3}; + connection.Stream.Write(buffer, 0, 3); + Assert.AreEqual(0, connection.BytesRead); + Assert.AreEqual(3, connection.BytesWritten); + + stream.Position = 0; + connection.Stream.ReadByte(); + connection.Stream.ReadByte(); + Assert.AreEqual(2, connection.BytesRead); + Assert.AreEqual(3, connection.BytesWritten); + } + + [TestMethod] + public void Protocols() + { + var connection = new PeerConnection(); + Assert.AreEqual(0, connection.Protocols.Count); + + connection.AddProtocol(new Identify1()); + Assert.AreEqual(1, connection.Protocols.Count); + + connection.AddProtocols(new IPeerProtocol[] {new Mplex67(), new Plaintext1()}); + Assert.AreEqual(3, connection.Protocols.Count); + } + + [TestMethod] + public void CreatesOneStatsStream() + { + var a = new MemoryStream(); + var b = new MemoryStream(); + var connection = new PeerConnection(); + Assert.AreEqual(null, connection.Stream); + + connection.Stream = a; + Assert.AreNotSame(a, connection.Stream); + + connection.Stream = b; + Assert.AreSame(b, connection.Stream); + } + } +} diff --git a/src/Lib.P2P.Tests/PeerManagerTest.cs b/src/Lib.P2P.Tests/PeerManagerTest.cs new file mode 100644 index 0000000000..06f98f82af --- /dev/null +++ b/src/Lib.P2P.Tests/PeerManagerTest.cs @@ -0,0 +1,173 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests +{ + [TestClass] + public class PeerManagerTest + { + private Peer self = new Peer + { + AgentVersion = "self", + Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCC5r4nQBtnd9qgjnG8fBN5+gnqIeWEIcUFUdCG4su/vrbQ1py8XGKNUBuDjkyTv25Gd3hlrtNJV3eOKZVSL8ePAgMBAAE=" + }; + + [TestMethod] + public void IsNotReachable() + { + var peer = new Peer {Id = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb"}; + var manager = new PeerManager {SwarmService = new SwarmService(null)}; + Assert.AreEqual(0, manager.DeadPeers.Count); + + manager.SetNotReachable(peer); + Assert.IsTrue(manager.DeadPeers.ContainsKey(peer)); + Assert.AreEqual(1, manager.DeadPeers.Count); + + manager.SetNotReachable(peer); + Assert.IsTrue(manager.DeadPeers.ContainsKey(peer)); + Assert.AreEqual(1, manager.DeadPeers.Count); + + manager.SetReachable(peer); + Assert.IsFalse(manager.DeadPeers.ContainsKey(peer)); + Assert.AreEqual(0, manager.DeadPeers.Count); + } + + [TestMethod] + public void BlackListsThePeer() + { + var peer = new Peer {Id = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb"}; + var manager = new PeerManager {SwarmService = new SwarmService(null)}; + Assert.AreEqual(0, manager.DeadPeers.Count); + + manager.SetNotReachable(peer); + Assert.IsFalse( + manager.SwarmService.IsAllowed((MultiAddress) "/p2p/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb")); + + manager.SetReachable(peer); + Assert.IsTrue( + manager.SwarmService.IsAllowed((MultiAddress) "/p2p/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb")); + } + + [TestMethod] + public async Task Backoff_Increases() + { + var peer = new Peer + { + Id = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTxx", + Addresses = new MultiAddress[] + { + "/ip4/127.0.0.1/tcp/4040/ipfs/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTxx" + } + }; + var swarm = new SwarmService(self); + var manager = new PeerManager + { + SwarmService = swarm, + InitialBackoff = TimeSpan.FromMilliseconds(100), + }; + Assert.AreEqual(0, manager.DeadPeers.Count); + + try + { + await swarm.StartAsync(); + await manager.StartAsync(); + try + { + await swarm.ConnectAsync(peer); + } + catch + { + // ignored + } + + Assert.AreEqual(1, manager.DeadPeers.Count); + + var end = DateTime.Now + TimeSpan.FromSeconds(4); + while (DateTime.Now <= end) + if (manager.DeadPeers[peer].Backoff > manager.InitialBackoff) + return; + Assert.Fail("backoff did not increase"); + } + finally + { + await swarm.StopAsync(); + await manager.StopAsync(); + } + } + + [TestMethod] + public async Task PermanentlyDead() + { + var peer = new Peer + { + Id = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb", + Addresses = new MultiAddress[] + { + "/ip4/127.0.0.1/tcp/4040/ipfs/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb" + } + }; + var swarm = new SwarmService(self); + var manager = new PeerManager + { + SwarmService = swarm, + InitialBackoff = TimeSpan.FromMilliseconds(100), + MaxBackoff = TimeSpan.FromMilliseconds(200), + }; + Assert.AreEqual(0, manager.DeadPeers.Count); + + try + { + await swarm.StartAsync(); + await manager.StartAsync(); + try + { + await swarm.ConnectAsync(peer); + } + catch + { + // ignored + } + + Assert.AreEqual(1, manager.DeadPeers.Count); + + var end = DateTime.Now + TimeSpan.FromSeconds(6); + while (DateTime.Now <= end) + if (manager.DeadPeers[peer].NextAttempt == DateTime.MaxValue) + return; + Assert.Fail("not truely dead"); + } + finally + { + await swarm.StopAsync(); + await manager.StopAsync(); + } + } + } +} diff --git a/src/Lib.P2P.Tests/PeerTest.cs b/src/Lib.P2P.Tests/PeerTest.cs new file mode 100644 index 0000000000..1475a0c63e --- /dev/null +++ b/src/Lib.P2P.Tests/PeerTest.cs @@ -0,0 +1,163 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests +{ + [TestClass] + public sealed class PeerTest + { + private const string MarsId = "QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + private const string PlutoId = "QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM"; + + private const string MarsPublicKey = + "CAASogEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKGUtbRQf+a9SBHFEruNAUatS/tsGUnHuCtifGrlbYPELD3UyyhWf/FYczBCavx3i8hIPEW2jQv4ehxQxi/cg9SHswZCQblSi0ucwTBFr8d40JEiyB9CcapiMdFQxdMgGvXEOQdLz1pz+UPUDojkdKZq8qkkeiBn7KlAoGEocnmpAgMBAAE="; + + private static string marsAddress = + "/ip4/10.1.10.10/tcp/29087/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + + [TestMethod] + public new void ToString() + { + Assert.AreEqual("", new Peer().ToString()); + Assert.AreEqual(MarsId, new Peer {Id = MarsId}.ToString()); + } + + [TestMethod] + public void DefaultValues() + { + var peer = new Peer(); + Assert.AreEqual(null, peer.Id); + Assert.AreEqual(0, peer.Addresses.Count()); + Assert.AreEqual("unknown/0.0", peer.ProtocolVersion); + Assert.AreEqual("unknown/0.0", peer.AgentVersion); + Assert.AreEqual(null, peer.PublicKey); + Assert.AreEqual(false, peer.IsValid()); // missing peer ID + Assert.AreEqual(null, peer.ConnectedAddress); + Assert.IsFalse(peer.Latency.HasValue); + } + + [TestMethod] + public void ConnectedPeer() + { + var peer = new Peer + { + ConnectedAddress = new MultiAddress(marsAddress), + Latency = TimeSpan.FromHours(3.03 * 2) + }; + Assert.AreEqual(marsAddress, peer.ConnectedAddress.ToString()); + Assert.AreEqual(3.03 * 2, peer.Latency.Value.TotalHours); + } + + [TestMethod] + public void Validation_No_Id() + { + var peer = new Peer(); + Assert.AreEqual(false, peer.IsValid()); + } + + [TestMethod] + public void Validation_With_Id() + { + Peer peer = MarsId; + Assert.AreEqual(true, peer.IsValid()); + } + + [TestMethod] + public void Validation_With_Id_Pubkey() + { + var peer = new Peer + { + Id = MarsId, + PublicKey = MarsPublicKey + }; + Assert.AreEqual(true, peer.IsValid()); + } + + [TestMethod] + public void Validation_With_Id_Invalid_Pubkey() + { + var peer = new Peer + { + Id = PlutoId, + PublicKey = MarsPublicKey + }; + Assert.AreEqual(false, peer.IsValid()); + } + + [TestMethod] + public void Value_Equality() + { + var a0 = new Peer {Id = MarsId}; + var a1 = new Peer {Id = MarsId}; + var b = new Peer {Id = PlutoId}; + Peer c = null; + Peer d = null; + + Assert.IsTrue(c == d); + Assert.IsFalse(c == b); + Assert.IsFalse(b == c); + + Assert.IsFalse(c != d); + Assert.IsTrue(c != b); + Assert.IsTrue(b != c); + +#pragma warning disable 1718 + Assert.IsTrue(a0 == a0); + Assert.IsTrue(a0 == a1); + Assert.IsFalse(a0 == b); + +#pragma warning disable 1718 + Assert.IsFalse(a0 != a0); + Assert.IsFalse(a0 != a1); + Assert.IsTrue(a0 != b); + + Assert.IsTrue(a0.Equals(a0)); + Assert.IsTrue(a0.Equals(a1)); + Assert.IsFalse(a0.Equals(b)); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0.GetHashCode(), a0.GetHashCode()); + Assert.AreEqual(a0.GetHashCode(), a1.GetHashCode()); + Assert.AreNotEqual(a0.GetHashCode(), b.GetHashCode()); + } + + [TestMethod] + public void Implicit_Conversion_From_String() + { + Peer a = MarsId; + Assert.IsInstanceOfType(a, typeof(Peer)); + } + } +} diff --git a/src/Lib.P2P.Tests/PolicyTest.cs b/src/Lib.P2P.Tests/PolicyTest.cs new file mode 100644 index 0000000000..1e3944c731 --- /dev/null +++ b/src/Lib.P2P.Tests/PolicyTest.cs @@ -0,0 +1,45 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests +{ + [TestClass] + public class PolicyTest + { + [TestMethod] + public void Always() + { + var policy = new PolicyAlways(); + Assert.IsTrue(policy.IsAllowed("foo")); + } + + [TestMethod] + public void Never() + { + var policy = new PolicyNever(); + Assert.IsFalse(policy.IsAllowed("foo")); + } + } +} diff --git a/src/Lib.P2P.Tests/Protocols/Identify1Test.cs b/src/Lib.P2P.Tests/Protocols/Identify1Test.cs new file mode 100644 index 0000000000..233f682f5d --- /dev/null +++ b/src/Lib.P2P.Tests/Protocols/Identify1Test.cs @@ -0,0 +1,153 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Protocols; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests.Protocols +{ + [TestClass] + public class Identitfy1Test + { + [TestMethod] + public async Task RoundTrip() + { + var peerA = new Peer + { + Addresses = new MultiAddress[] + { + "/ip4/127.0.0.1/tcp/4002/ipfs/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb" + }, + AgentVersion = "agent/1", + Id = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb", + ProtocolVersion = "protocol/1", + PublicKey = + "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfBYU9c0n28u02N/XCJY8yIsRqRVO5Zw+6kDHCremt2flHT4AaWnwGLAG9YyQJbRTvWN9nW2LK7Pv3uoIlvUSTnZEP0SXB5oZeqtxUdi6tuvcyqTIfsUSanLQucYITq8Qw3IMBzk+KpWNm98g9A/Xy30MkUS8mrBIO9pHmIZa55fvclDkTvLxjnGWA2avaBfJvHgMSTu0D2CQcmJrvwyKMhLCSIbQewZd2V7vc6gtxbRovKlrIwDTmDBXbfjbLljOuzg2yBLyYxXlozO9blpttbnOpU4kTspUVJXglmjsv7YSIJS3UKt3544l/srHbqlwC5CgOgjlwNfYPadO8kmBfAgMBAAE=" + }; + var peerB = new Peer(); + var ms = new MemoryStream(); + var connection = new PeerConnection + { + LocalPeer = peerA, + RemotePeer = peerB, + Stream = ms + }; + + // Generate identify msg. + var identify = new Identify1(); + await identify.ProcessMessageAsync(connection, ms); + + // Process identify msg. + ms.Position = 0; + await identify.UpdateRemotePeerAsync(peerB, ms, CancellationToken.None); + + Assert.AreEqual(peerA.AgentVersion, peerB.AgentVersion); + Assert.AreEqual(peerA.Id, peerB.Id); + Assert.AreEqual(peerA.ProtocolVersion, peerB.ProtocolVersion); + Assert.AreEqual(peerA.PublicKey, peerB.PublicKey); + CollectionAssert.AreEqual(peerA.Addresses.ToArray(), peerB.Addresses.ToArray()); + } + + [TestMethod] + public async Task InvalidPublicKey() + { + var peerA = new Peer + { + Addresses = new MultiAddress[] + { + "/ip4/127.0.0.1/tcp/4002/ipfs/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb" + }, + AgentVersion = "agent/1", + Id = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb", + ProtocolVersion = "protocol/1", + PublicKey = + "BADSpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfBYU9c0n28u02N/XCJY8yIsRqRVO5Zw+6kDHCremt2flHT4AaWnwGLAG9YyQJbRTvWN9nW2LK7Pv3uoIlvUSTnZEP0SXB5oZeqtxUdi6tuvcyqTIfsUSanLQucYITq8Qw3IMBzk+KpWNm98g9A/Xy30MkUS8mrBIO9pHmIZa55fvclDkTvLxjnGWA2avaBfJvHgMSTu0D2CQcmJrvwyKMhLCSIbQewZd2V7vc6gtxbRovKlrIwDTmDBXbfjbLljOuzg2yBLyYxXlozO9blpttbnOpU4kTspUVJXglmjsv7YSIJS3UKt3544l/srHbqlwC5CgOgjlwNfYPadO8kmBfAgMBAAE=" + }; + var peerB = new Peer + { + Id = peerA.Id + }; + var ms = new MemoryStream(); + var connection = new PeerConnection + { + LocalPeer = peerA, + RemotePeer = peerB, + Stream = ms + }; + + // Generate identify msg. + var identify = new Identify1(); + await identify.ProcessMessageAsync(connection, ms); + + // Process identify msg. + ms.Position = 0; + ExceptionAssert.Throws(() => + { + identify.UpdateRemotePeerAsync(peerB, ms, CancellationToken.None).Wait(); + }); + } + + [TestMethod] + public async Task MustHavePublicKey() + { + var peerA = new Peer + { + Addresses = new MultiAddress[] + { + "/ip4/127.0.0.1/tcp/4002/ipfs/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb" + }, + AgentVersion = "agent/1", + Id = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb", + ProtocolVersion = "protocol/1", + PublicKey = "" + }; + var peerB = new Peer + { + Id = peerA.Id + }; + var ms = new MemoryStream(); + var connection = new PeerConnection + { + LocalPeer = peerA, + RemotePeer = peerB, + Stream = ms + }; + + // Generate identify msg. + var identify = new Identify1(); + await identify.ProcessMessageAsync(connection, ms); + + // Process identify msg. + ms.Position = 0; + ExceptionAssert.Throws(() => + { + identify.UpdateRemotePeerAsync(peerB, ms, CancellationToken.None).Wait(); + }); + } + } +} diff --git a/src/Lib.P2P.Tests/Protocols/MessageTest.cs b/src/Lib.P2P.Tests/Protocols/MessageTest.cs new file mode 100644 index 0000000000..5c02f9fd4c --- /dev/null +++ b/src/Lib.P2P.Tests/Protocols/MessageTest.cs @@ -0,0 +1,57 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading.Tasks; +using Lib.P2P.Protocols; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.Protocols +{ + [TestClass] + public class MessageTest + { + [TestMethod] + public async Task Encoding() + { + var ms = new MemoryStream(); + await Message.WriteAsync("a", ms); + var buf = ms.ToArray(); + Assert.AreEqual(3, buf.Length); + Assert.AreEqual(2, buf[0]); + Assert.AreEqual((byte) 'a', buf[1]); + Assert.AreEqual((byte) '\n', buf[2]); + } + + [TestMethod] + public async Task RoundTrip() + { + var msg = "/foobar/0.42.0"; + var ms = new MemoryStream(); + await Message.WriteAsync(msg, ms); + ms.Position = 0; + var result = await Message.ReadStringAsync(ms); + Assert.AreEqual(msg, result); + } + } +} diff --git a/src/Lib.P2P.Tests/Protocols/Ping1Test.cs b/src/Lib.P2P.Tests/Protocols/Ping1Test.cs new file mode 100644 index 0000000000..3ecd35512c --- /dev/null +++ b/src/Lib.P2P.Tests/Protocols/Ping1Test.cs @@ -0,0 +1,105 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Threading.Tasks; +using Lib.P2P.Protocols; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.Protocols +{ + [TestClass] + public class PingTest + { + private Peer self = new Peer + { + AgentVersion = "self", + Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCC5r4nQBtnd9qgjnG8fBN5+gnqIeWEIcUFUdCG4su/vrbQ1py8XGKNUBuDjkyTv25Gd3hlrtNJV3eOKZVSL8ePAgMBAAE=" + }; + + private Peer other = new Peer + { + AgentVersion = "other", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + + [TestMethod] + public async Task MultiAddress() + { + var swarmB = new SwarmService(other); + await swarmB.StartAsync(); + var pingB = new Ping1(swarmB); + await pingB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var swarm = new SwarmService(self); + await swarm.StartAsync(); + var pingA = new Ping1(swarm); + await pingA.StartAsync(); + try + { + await swarm.ConnectAsync(peerBAddress); + var result = await pingA.PingAsync(other.Id, 4); + Assert.IsTrue(result.All(r => r.Success)); + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + await pingB.StopAsync(); + await pingA.StopAsync(); + } + } + + [TestMethod] + public async Task PeerId() + { + var swarmB = new SwarmService(other); + await swarmB.StartAsync(); + var pingB = new Ping1(swarmB); + await pingB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var swarm = new SwarmService(self); + await swarm.StartAsync(); + var pingA = new Ping1(swarm); + await pingA.StartAsync(); + try + { + var result = await pingA.PingAsync(peerBAddress, 4); + Assert.IsTrue(result.All(r => r.Success)); + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + await pingB.StopAsync(); + await pingA.StopAsync(); + } + } + } +} diff --git a/src/Lib.P2P.Tests/Protocols/PingResultTest.cs b/src/Lib.P2P.Tests/Protocols/PingResultTest.cs new file mode 100644 index 0000000000..4761596b47 --- /dev/null +++ b/src/Lib.P2P.Tests/Protocols/PingResultTest.cs @@ -0,0 +1,48 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Lib.P2P.Protocols; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.Protocols +{ + [TestClass] + public class PingResultTest + { + [TestMethod] + public void Properties() + { + var time = TimeSpan.FromSeconds(3); + var r = new PingResult + { + Success = true, + Text = "ping", + Time = time + }; + Assert.AreEqual(true, r.Success); + Assert.AreEqual("ping", r.Text); + Assert.AreEqual(time, r.Time); + } + } +} diff --git a/src/Lib.P2P.Tests/Protocols/ProtocolRegistryTest.cs b/src/Lib.P2P.Tests/Protocols/ProtocolRegistryTest.cs new file mode 100644 index 0000000000..640a61f51a --- /dev/null +++ b/src/Lib.P2P.Tests/Protocols/ProtocolRegistryTest.cs @@ -0,0 +1,39 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Lib.P2P.Protocols; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.Protocols +{ + [TestClass] + public class ProtocolRegistryTest + { + [TestMethod] + public void PreRegistered() + { + CollectionAssert.Contains(ProtocolRegistry.Protocols.Keys, "/multistream/1.0.0"); + CollectionAssert.Contains(ProtocolRegistry.Protocols.Keys, "/plaintext/1.0.0"); + } + } +} diff --git a/src/Lib.P2P.Tests/Protocols/VersionedNameTest.cs b/src/Lib.P2P.Tests/Protocols/VersionedNameTest.cs new file mode 100644 index 0000000000..e6fda69cc2 --- /dev/null +++ b/src/Lib.P2P.Tests/Protocols/VersionedNameTest.cs @@ -0,0 +1,130 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Linq; +using Lib.P2P.Protocols; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.Protocols +{ + [TestClass] + public class VersionedNameTest + { + [TestMethod] + public void Parsing() + { + var vn = VersionedName.Parse("/multistream/1.0.0"); + Assert.AreEqual("multistream", vn.Name); + Assert.AreEqual("1.0.0", vn.Version.ToString()); + + vn = VersionedName.Parse("/ipfs/id/1.0.0"); + Assert.AreEqual("ipfs/id", vn.Name); + Assert.AreEqual("1.0.0", vn.Version.ToString()); + } + + [TestMethod] + public void Stringing() + { + var vn = new VersionedName {Name = "x", Version = new Semver.SemVersion(0, 42)}; + Assert.AreEqual("/x/0.42.0", vn.ToString()); + } + + [TestMethod] + public void Value_Equality() + { + var a0 = VersionedName.Parse("/x/1.0.0"); + var a1 = VersionedName.Parse("/x/1.0.0"); + var b = VersionedName.Parse("/x/1.1.0"); + VersionedName c = null; + VersionedName d = null; + + Assert.IsTrue(c == d); + Assert.IsFalse(c == b); + Assert.IsFalse(b == c); + + Assert.IsFalse(c != d); + Assert.IsTrue(c != b); + Assert.IsTrue(b != c); + +#pragma warning disable 1718 + Assert.IsTrue(a0 == a0); + Assert.IsTrue(a0 == a1); + Assert.IsFalse(a0 == b); + +#pragma warning disable 1718 + Assert.IsFalse(a0 != a0); + Assert.IsFalse(a0 != a1); + Assert.IsTrue(a0 != b); + + Assert.IsTrue(a0.Equals(a0)); + Assert.IsTrue(a0.Equals(a1)); + Assert.IsFalse(a0.Equals(b)); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0.GetHashCode(), a0.GetHashCode()); + Assert.AreEqual(a0.GetHashCode(), a1.GetHashCode()); + Assert.AreNotEqual(a0.GetHashCode(), b.GetHashCode()); + } + + [TestMethod] + public void Comparing() + { + var a0 = VersionedName.Parse("/x/1.0.0"); + var a1 = VersionedName.Parse("/x/1.0.0"); + var b = VersionedName.Parse("/x/1.1.0"); + var c = VersionedName.Parse("/y/0.42.0"); + + Assert.AreEqual(0, a0.CompareTo(a1)); + Assert.AreEqual(0, a1.CompareTo(a0)); + + Assert.AreEqual(1, b.CompareTo(a0)); + Assert.AreEqual(-1, a0.CompareTo(b)); + + Assert.AreEqual(1, c.CompareTo(b)); + Assert.AreEqual(-1, b.CompareTo(c)); + } + + [TestMethod] + public void Ordering() + { + var names = new List + { + VersionedName.Parse("/x/1.0.0"), + VersionedName.Parse("/x/1.1.0"), + VersionedName.Parse("/y/0.42.0"), + }; + var ordered = names.OrderByDescending(n => n).ToArray(); + Assert.AreEqual("/y/0.42.0", ordered[0].ToString()); + Assert.AreEqual("/x/1.1.0", ordered[1].ToString()); + Assert.AreEqual("/x/1.0.0", ordered[2].ToString()); + } + } +} diff --git a/src/Lib.P2P.Tests/PubSub/FloodRouterTest.cs b/src/Lib.P2P.Tests/PubSub/FloodRouterTest.cs new file mode 100644 index 0000000000..7bf367e57b --- /dev/null +++ b/src/Lib.P2P.Tests/PubSub/FloodRouterTest.cs @@ -0,0 +1,335 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.PubSub; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.PubSub +{ + [TestClass] + public class FloodRouterTest + { + private Peer self = new Peer + { + AgentVersion = "self", + Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCC5r4nQBtnd9qgjnG8fBN5+gnqIeWEIcUFUdCG4su/vrbQ1py8XGKNUBuDjkyTv25Gd3hlrtNJV3eOKZVSL8ePAgMBAAE=" + }; + + private Peer other = new Peer + { + AgentVersion = "other", + Id = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb", + PublicKey = + "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfBYU9c0n28u02N/XCJY8yIsRqRVO5Zw+6kDHCremt2flHT4AaWnwGLAG9YyQJbRTvWN9nW2LK7Pv3uoIlvUSTnZEP0SXB5oZeqtxUdi6tuvcyqTIfsUSanLQucYITq8Qw3IMBzk+KpWNm98g9A/Xy30MkUS8mrBIO9pHmIZa55fvclDkTvLxjnGWA2avaBfJvHgMSTu0D2CQcmJrvwyKMhLCSIbQewZd2V7vc6gtxbRovKlrIwDTmDBXbfjbLljOuzg2yBLyYxXlozO9blpttbnOpU4kTspUVJXglmjsv7YSIJS3UKt3544l/srHbqlwC5CgOgjlwNfYPadO8kmBfAgMBAAE=" + }; + + private Peer other1 = new Peer + { + AgentVersion = "other1", + Id = "QmYSj5nkpHaJG6hDof33fv3YHnQfpFTNAd8jZ5GssgPygn", + PublicKey = + "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQC8s23axzV5S/fUoJ1+MT9fH1SzlDwqwdKoIirYAmvHnv6dyoaC7gMeHDJIc2gNnrvpdAoXyPxBS2Oysv/iHzseVi2kYvyU9pD5ZtiorzpV5oOXMfIfgGygXbiIk/DVQWD6Sq8flHY8ht+z69h9JL+Dj/aMfEzY5RoznJkikumoCn7QI6zvPZd9OPd7OyqcCZ31RThtIxrFd0YkHN+VV9pCq4iBfhMt8Ocy0RS/yrqaGE4PX2VsjExBmShEFnTFlhy0Mh4QhBLLquQH0aQEk2s5mZtwh7bKeW84zC0zIGWzcHrwVsHb+Z2/IXDTWNIlNGc/cCV7vAM1EgK1oQVf04NLAgMBAAE=", + }; + + [TestMethod] + public void Defaults() + { + var router = new FloodRouter(new SwarmService(self)); + Assert.AreEqual("/floodsub/1.0.0", router.ToString()); + } + + [TestMethod] + public void RemoteSubscriptions() + { + var router = new FloodRouter(new SwarmService(self)); + + var sub = new Subscription { Topic = "topic", Subscribe = true }; + router.ProcessSubscription(sub, other); + Assert.AreEqual(1, router.RemoteTopics.GetPeers("topic").Count()); + + var can = new Subscription { Topic = "topic", Subscribe = false }; + router.ProcessSubscription(can, other); + Assert.AreEqual(0, router.RemoteTopics.GetPeers("topic").Count()); + } + + [TestMethod] + public async Task Sends_Hello_OnConnect() + { + var topic = Guid.NewGuid().ToString(); + + var swarm1 = new SwarmService(self); + var router1 = new FloodRouter(swarm1); + var ns1 = new PubSubService(self, new[] { router1 }); + await swarm1.StartAsync(); + await ns1.StartAsync(); + + var swarm2 = new SwarmService(other); + var router2 = new FloodRouter(swarm2); + var ns2 = new PubSubService(other, new[] { router2 }); + await swarm2.StartAsync(); + await ns2.StartAsync(); + + try + { + await swarm1.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + await swarm2.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var cs = new CancellationTokenSource(); + await ns1.SubscribeAsync(topic, msg => { }, cs.Token); + await swarm1.ConnectAsync(other, cs.Token); + + var peers = new Peer[0]; + var endTime = DateTime.Now.AddSeconds(3); + while (peers.Length == 0) + { + if (DateTime.Now > endTime) + { + Assert.Fail("timeout"); + } + + await Task.Delay(100, cs.Token); + peers = (await ns2.PeersAsync(topic, cs.Token)).ToArray(); + } + + CollectionAssert.Contains(peers, self); + } + finally + { + await swarm1.StopAsync(); + await ns1.StopAsync(); + + await swarm2.StopAsync(); + await ns2.StopAsync(); + } + } + + [TestMethod] + public async Task Sends_NewSubscription() + { + var topic = Guid.NewGuid().ToString(); + + var swarm1 = new SwarmService(self); + var router1 = new FloodRouter(swarm1); + var ns1 = new PubSubService(self, new[] { router1 }); + await swarm1.StartAsync(); + await ns1.StartAsync(); + + var swarm2 = new SwarmService(other); + var router2 = new FloodRouter(swarm2); + var ns2 = new PubSubService(other, new[] { router2 }); + await swarm2.StartAsync(); + await ns2.StartAsync(); + + try + { + await swarm1.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + await swarm2.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var cs = new CancellationTokenSource(); + await swarm1.ConnectAsync(other, cs.Token); + await ns1.SubscribeAsync(topic, msg => { }, cs.Token); + + var peers = new Peer[0]; + var endTime = DateTime.Now.AddSeconds(3); + while (peers.Length == 0) + { + if (DateTime.Now > endTime) + { + Assert.Fail("timeout"); + } + + await Task.Delay(100, cs.Token); + peers = (await ns2.PeersAsync(topic, cs.Token)).ToArray(); + } + + CollectionAssert.Contains(peers, self); + } + finally + { + await swarm1.StopAsync(); + await ns1.StopAsync(); + + await swarm2.StopAsync(); + await ns2.StopAsync(); + } + } + + [TestMethod] + public async Task Sends_CancelledSubscription() + { + var topic = Guid.NewGuid().ToString(); + + var swarm1 = new SwarmService(self); + var router1 = new FloodRouter(swarm1); + var ns1 = new PubSubService(self, new[] { router1 }); + await swarm1.StartAsync(); + await ns1.StartAsync(); + + var swarm2 = new SwarmService(other); + var router2 = new FloodRouter(swarm2); + var ns2 = new PubSubService(other, new[] { router2 }); + await swarm2.StartAsync(); + await ns2.StartAsync(); + + try + { + await swarm1.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + await swarm2.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var cs = new CancellationTokenSource(); + await swarm1.ConnectAsync(other, cs.Token); + await ns1.SubscribeAsync(topic, msg => { }, cs.Token); + + var peers = new Peer[0]; + var endTime = DateTime.Now.AddSeconds(3); + while (peers.Length == 0) + { + if (DateTime.Now > endTime) + { + Assert.Fail("timeout"); + } + + await Task.Delay(100, cs.Token); + peers = (await ns2.PeersAsync(topic, cs.Token)).ToArray(); + } + + CollectionAssert.Contains(peers, self); + + cs.Cancel(); + peers = new Peer[0]; + endTime = DateTime.Now.AddSeconds(3); + while (peers.Length != 0) + { + if (DateTime.Now > endTime) + { + Assert.Fail("timeout"); + } + + await Task.Delay(100, cs.Token); + peers = (await ns2.PeersAsync(topic, cs.Token)).ToArray(); + } + } + finally + { + await swarm1.StopAsync(); + await ns1.StopAsync(); + + await swarm2.StopAsync(); + await ns2.StopAsync(); + } + } + + [TestMethod] + public async Task Relays_PublishedMessage() + { + var topic = Guid.NewGuid().ToString(); + + var swarm1 = new SwarmService(self); + var router1 = new FloodRouter(swarm1); + var ns1 = new PubSubService(self, new[] { router1 }); + await swarm1.StartAsync(); + await ns1.StartAsync(); + + var swarm2 = new SwarmService(other); + var router2 = new FloodRouter(swarm2); + var ns2 = new PubSubService(other, new[] { router2 }); + await swarm2.StartAsync(); + await ns2.StartAsync(); + + var swarm3 = new SwarmService(other1); + var router3 = new FloodRouter(swarm3); + var ns3 = new PubSubService(other1, new[] { router3 }); + await swarm3.StartAsync(); + await ns3.StartAsync(); + + try + { + IPublishedMessage lastMessage2 = null; + IPublishedMessage lastMessage3 = null; + await swarm1.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + await swarm2.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + await swarm3.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var cs = new CancellationTokenSource(); + await ns2.SubscribeAsync(topic, msg => lastMessage2 = msg, cs.Token); + await ns3.SubscribeAsync(topic, msg => lastMessage3 = msg, cs.Token); + await swarm1.ConnectAsync(other, cs.Token); + await swarm3.ConnectAsync(other, cs.Token); + + var peers = new Peer[0]; + var endTime = DateTime.Now.AddSeconds(3); + while (peers.Length == 0) + { + if (DateTime.Now > endTime) + { + Assert.Fail("timeout"); + } + + await Task.Delay(100, cs.Token); + peers = (await ns2.PeersAsync(topic, cs.Token)).ToArray(); + } + + CollectionAssert.Contains(peers, other1); + + await ns1.PublishAsync(topic, new byte[] { 1 }, cs.Token); + endTime = DateTime.Now.AddSeconds(3); + while (lastMessage2 == null || lastMessage3 == null) + { + if (DateTime.Now > endTime) + { + Assert.Fail("timeout"); + } + + await Task.Delay(100, cs.Token); + } + + Assert.IsNotNull(lastMessage2); + Assert.AreEqual(self, lastMessage2.Sender); + CollectionAssert.AreEqual(new byte[] { 1 }, lastMessage2.DataBytes); + CollectionAssert.Contains(lastMessage2.Topics.ToArray(), topic); + + Assert.IsNotNull(lastMessage3); + Assert.AreEqual(self, lastMessage3.Sender); + CollectionAssert.AreEqual(new byte[] { 1 }, lastMessage3.DataBytes); + CollectionAssert.Contains(lastMessage3.Topics.ToArray(), topic); + } + finally + { + await swarm1.StopAsync(); + await ns1.StopAsync(); + + await swarm2.StopAsync(); + await ns2.StopAsync(); + + await swarm3.StopAsync(); + await ns3.StopAsync(); + } + } + } +} diff --git a/src/Lib.P2P.Tests/PubSub/NotificationServiceTest .cs b/src/Lib.P2P.Tests/PubSub/NotificationServiceTest .cs new file mode 100644 index 0000000000..064ab00dee --- /dev/null +++ b/src/Lib.P2P.Tests/PubSub/NotificationServiceTest .cs @@ -0,0 +1,240 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.PubSub; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.PubSub +{ + [TestClass] + public class NotificationServiceTest + { + private Peer self = new Peer + { + AgentVersion = "self", + Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCC5r4nQBtnd9qgjnG8fBN5+gnqIeWEIcUFUdCG4su/vrbQ1py8XGKNUBuDjkyTv25Gd3hlrtNJV3eOKZVSL8ePAgMBAAE=" + }; + + private Peer other1 = new Peer { Id = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" }; + private Peer other2 = new Peer { Id = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvUJ" }; + + [TestMethod] + public async Task MessageID_Increments() + { + var ns = new PubSubService(self, new[] { new LoopbackRouter() }); + await ns.StartAsync(); + try + { + var a = ns.CreateMessage("topic", new byte[0]); + var b = ns.CreateMessage("topic", new byte[0]); + Assert.IsTrue(string.Compare(b.MessageId, a.MessageId, StringComparison.Ordinal) > 0); + } + finally + { + await ns.StopAsync(); + } + } + + [TestMethod] + public async Task Publish() + { + var ns = new PubSubService(self, new[] { new LoopbackRouter() }); + await ns.StartAsync(); + try + { + await ns.PublishAsync("topic", "foo"); + await ns.PublishAsync("topic", new byte[] { 1, 2, 3 }); + await ns.PublishAsync("topic", new MemoryStream(new byte[] { 1, 2, 3 })); + Assert.AreEqual(3ul, ns.MesssagesPublished); + Assert.AreEqual(3ul, ns.MesssagesReceived); + } + finally + { + await ns.StopAsync(); + } + } + + [TestMethod] + public async Task Topics() + { + var ns = new PubSubService(self, new[] { new LoopbackRouter() }); + await ns.StartAsync(); + try + { + var topicA = Guid.NewGuid().ToString(); + var topicB = Guid.NewGuid().ToString(); + var csA = new CancellationTokenSource(); + var csB = new CancellationTokenSource(); + + await ns.SubscribeAsync(topicA, msg => { }, csA.Token); + await ns.SubscribeAsync(topicA, msg => { }, csA.Token); + await ns.SubscribeAsync(topicB, msg => { }, csB.Token); + + var topics = (await ns.SubscribedTopicsAsync(csA.Token)).ToArray(); + Assert.AreEqual(2, topics.Length); + CollectionAssert.Contains(topics, topicA); + CollectionAssert.Contains(topics, topicB); + + csA.Cancel(); + topics = (await ns.SubscribedTopicsAsync(csA.Token)).ToArray(); + Assert.AreEqual(1, topics.Length); + CollectionAssert.Contains(topics, topicB); + + csB.Cancel(); + topics = (await ns.SubscribedTopicsAsync(csA.Token)).ToArray(); + Assert.AreEqual(0, topics.Length); + } + finally + { + await ns.StopAsync(); + } + } + + [TestMethod] + public async Task Subscribe() + { + var ns = new PubSubService(self, new[] { new LoopbackRouter() }); + await ns.StartAsync(); + try + { + var topic = Guid.NewGuid().ToString(); + var cs = new CancellationTokenSource(); + var messageCount = 0; + await ns.SubscribeAsync(topic, msg => { ++messageCount; }, cs.Token); + await ns.SubscribeAsync(topic, msg => { ++messageCount; }, cs.Token); + + await ns.PublishAsync(topic, "", cs.Token); + Assert.AreEqual(2, messageCount); + } + finally + { + await ns.StopAsync(); + } + } + + [TestMethod] + public async Task Subscribe_HandlerExceptionIsIgnored() + { + var ns = new PubSubService(self, new[] { new LoopbackRouter() }); + await ns.StartAsync(); + try + { + var topic = Guid.NewGuid().ToString(); + var cs = new CancellationTokenSource(); + var messageCount = 0; + await ns.SubscribeAsync(topic, msg => + { + ++messageCount; + throw new Exception(); + }, cs.Token); + + await ns.PublishAsync(topic, "", cs.Token); + Assert.AreEqual(1, messageCount); + } + finally + { + await ns.StopAsync(); + } + } + + [TestMethod] + public async Task DuplicateMessagesAreIgnored() + { + var ns = new PubSubService(self, new[] { new LoopbackRouter() }); + ns.Routers.Add(new LoopbackRouter()); + await ns.StartAsync(); + try + { + var topic = Guid.NewGuid().ToString(); + var cs = new CancellationTokenSource(); + var messageCount = 0; + await ns.SubscribeAsync(topic, msg => { ++messageCount; }, cs.Token); + + await ns.PublishAsync(topic, "", cs.Token); + Assert.AreEqual(1, messageCount); + Assert.AreEqual(2ul, ns.MesssagesReceived); + Assert.AreEqual(1ul, ns.DuplicateMesssagesReceived); + } + finally + { + await ns.StopAsync(); + } + } + + [TestMethod] + public async Task SubscribedPeers_ForTopic() + { + var topic1 = Guid.NewGuid().ToString(); + var topic2 = Guid.NewGuid().ToString(); + var router = new FloodRouter(new SwarmService(self)); + var ns = new PubSubService(self, new[] { router }); + router.RemoteTopics.AddInterest(topic1, other1); + router.RemoteTopics.AddInterest(topic2, other2); + ns.Routers.Add(router); + await ns.StartAsync(); + try + { + var peers = (await ns.PeersAsync(topic1)).ToArray(); + Assert.AreEqual(1, peers.Length); + Assert.AreEqual(other1, peers[0]); + + peers = (await ns.PeersAsync(topic2)).ToArray(); + Assert.AreEqual(1, peers.Length); + Assert.AreEqual(other2, peers[0]); + } + finally + { + await ns.StopAsync(); + } + } + + [TestMethod] + public async Task SubscribedPeers_AllTopics() + { + var topic1 = Guid.NewGuid().ToString(); + var topic2 = Guid.NewGuid().ToString(); + var router = new FloodRouter(new SwarmService(self)); + var ns = new PubSubService(self, new[] { router }); + router.RemoteTopics.AddInterest(topic1, other1); + router.RemoteTopics.AddInterest(topic2, other2); + ns.Routers.Add(router); + await ns.StartAsync(); + try + { + var peers = (await ns.PeersAsync()).ToArray(); + Assert.AreEqual(2, peers.Length); + } + finally + { + await ns.StopAsync(); + } + } + } +} diff --git a/src/Lib.P2P.Tests/PubSub/PublishedMessageTest.cs b/src/Lib.P2P.Tests/PubSub/PublishedMessageTest.cs new file mode 100644 index 0000000000..f058782efc --- /dev/null +++ b/src/Lib.P2P.Tests/PubSub/PublishedMessageTest.cs @@ -0,0 +1,97 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using Lib.P2P.PubSub; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using ProtoBuf; + +namespace Lib.P2P.Tests.PubSub +{ + [TestClass] + public sealed class PublishedMessageTest + { + private readonly Peer _self = new Peer {Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH"}; + private readonly Peer _other = new Peer {Id = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"}; + + [TestMethod] + public void RoundTrip() + { + var a = new PublishedMessage + { + Topics = new[] {"topic"}, + Sender = _self, + SequenceNumber = new byte[] {1, 2, 3, 4, 5, 6, 7, 8}, + DataBytes = new byte[] {0, 1, 0xfe, 0xff} + }; + var ms = new MemoryStream(); + Serializer.Serialize(ms, a); + ms.Position = 0; + var b = Serializer.Deserialize(ms); + + CollectionAssert.AreEqual(a.Topics.ToArray(), b.Topics.ToArray()); + Assert.AreEqual(a.Sender, b.Sender); + CollectionAssert.AreEqual(a.SequenceNumber, b.SequenceNumber); + CollectionAssert.AreEqual(a.DataBytes, b.DataBytes); + Assert.AreEqual(a.DataBytes.Length, a.Size); + Assert.AreEqual(b.DataBytes.Length, b.Size); + } + + [TestMethod] + public void MessageID_Is_Unique() + { + var a = new PublishedMessage + { + Topics = new[] {"topic"}, + Sender = _self, + SequenceNumber = new byte[] {1, 2, 3, 4, 5, 6, 7, 8}, + DataBytes = new byte[] {0, 1, 0xfe, 0xff} + }; + var b = new PublishedMessage + { + Topics = new[] {"topic"}, + Sender = _other, + SequenceNumber = new byte[] {1, 2, 3, 4, 5, 6, 7, 8}, + DataBytes = new byte[] {0, 1, 0xfe, 0xff} + }; + + Assert.AreNotEqual(a.MessageId, b.MessageId); + } + + [TestMethod] + [ExpectedException(typeof(NotSupportedException))] + public void CidNotSupported() + { + var _ = new PublishedMessage().Id; + } + + [TestMethod] + public void DataStream() + { + var msg = new PublishedMessage {DataBytes = new byte[] {1}}; + Assert.AreEqual(1, msg.DataStream.ReadByte()); + } + } +} diff --git a/src/Lib.P2P.Tests/PubSub/TopicManagerTest.cs b/src/Lib.P2P.Tests/PubSub/TopicManagerTest.cs new file mode 100644 index 0000000000..34903bb7b3 --- /dev/null +++ b/src/Lib.P2P.Tests/PubSub/TopicManagerTest.cs @@ -0,0 +1,144 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using Lib.P2P.PubSub; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.PubSub +{ + [TestClass] + public class TopicManagerTest + { + private Peer a = new Peer {Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH"}; + private Peer b = new Peer {Id = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ"}; + + [TestMethod] + public void Adding() + { + var topics = new TopicManager(); + Assert.AreEqual(0, topics.GetPeers("alpha").Count()); + + topics.AddInterest("alpha", a); + Assert.AreEqual(a, topics.GetPeers("alpha").First()); + + topics.AddInterest("alpha", b); + var peers = topics.GetPeers("alpha").ToArray(); + CollectionAssert.Contains(peers, a); + CollectionAssert.Contains(peers, b); + } + + [TestMethod] + public void Adding_Duplicate() + { + var topics = new TopicManager(); + Assert.AreEqual(0, topics.GetPeers("alpha").Count()); + + topics.AddInterest("alpha", a); + Assert.AreEqual(1, topics.GetPeers("alpha").Count()); + + topics.AddInterest("alpha", a); + Assert.AreEqual(1, topics.GetPeers("alpha").Count()); + + topics.AddInterest("alpha", b); + Assert.AreEqual(2, topics.GetPeers("alpha").Count()); + } + + [TestMethod] + public void Removing() + { + var topics = new TopicManager(); + Assert.AreEqual(0, topics.GetPeers("alpha").Count()); + + topics.AddInterest("alpha", a); + topics.AddInterest("alpha", b); + Assert.AreEqual(2, topics.GetPeers("alpha").Count()); + + topics.RemoveInterest("alpha", a); + Assert.AreEqual(b, topics.GetPeers("alpha").First()); + Assert.AreEqual(1, topics.GetPeers("alpha").Count()); + + topics.RemoveInterest("alpha", a); + Assert.AreEqual(b, topics.GetPeers("alpha").First()); + Assert.AreEqual(1, topics.GetPeers("alpha").Count()); + + topics.RemoveInterest("alpha", b); + Assert.AreEqual(0, topics.GetPeers("alpha").Count()); + + topics.RemoveInterest("beta", b); + Assert.AreEqual(0, topics.GetPeers("beta").Count()); + } + + [TestMethod] + public void Clearing_Peers() + { + var topics = new TopicManager(); + Assert.AreEqual(0, topics.GetPeers("alpha").Count()); + Assert.AreEqual(0, topics.GetPeers("beta").Count()); + + topics.AddInterest("alpha", a); + topics.AddInterest("beta", a); + topics.AddInterest("beta", b); + Assert.AreEqual(1, topics.GetPeers("alpha").Count()); + Assert.AreEqual(2, topics.GetPeers("beta").Count()); + + topics.Clear(a); + Assert.AreEqual(0, topics.GetPeers("alpha").Count()); + Assert.AreEqual(1, topics.GetPeers("beta").Count()); + } + + [TestMethod] + public void Clearing() + { + var topics = new TopicManager(); + Assert.AreEqual(0, topics.GetPeers("alpha").Count()); + Assert.AreEqual(0, topics.GetPeers("beta").Count()); + + topics.AddInterest("alpha", a); + topics.AddInterest("beta", b); + Assert.AreEqual(1, topics.GetPeers("alpha").Count()); + Assert.AreEqual(1, topics.GetPeers("beta").Count()); + + topics.Clear(); + Assert.AreEqual(0, topics.GetPeers("alpha").Count()); + Assert.AreEqual(0, topics.GetPeers("beta").Count()); + } + + [TestMethod] + public void PeerTopics() + { + var tm = new TopicManager(); + tm.AddInterest("alpha", a); + CollectionAssert.AreEquivalent(new[] {"alpha"}, tm.GetTopics(a).ToArray()); + CollectionAssert.AreEquivalent(new string[0], tm.GetTopics(b).ToArray()); + + tm.AddInterest("beta", a); + CollectionAssert.AreEquivalent(new[] {"alpha", "beta"}, tm.GetTopics(a).ToArray()); + CollectionAssert.AreEquivalent(new string[0], tm.GetTopics(b).ToArray()); + + tm.AddInterest("beta", b); + CollectionAssert.AreEquivalent(new[] {"alpha", "beta"}, tm.GetTopics(a).ToArray()); + CollectionAssert.AreEquivalent(new[] {"beta"}, tm.GetTopics(b).ToArray()); + } + } +} diff --git a/src/Lib.P2P.Tests/Routing/ContentRouterTest.cs b/src/Lib.P2P.Tests/Routing/ContentRouterTest.cs new file mode 100644 index 0000000000..8960872a4b --- /dev/null +++ b/src/Lib.P2P.Tests/Routing/ContentRouterTest.cs @@ -0,0 +1,135 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using Lib.P2P.Routing; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests.Routing +{ + [TestClass] + public class ContentRouterTest + { + private Peer self = new Peer + { + AgentVersion = "self", + Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCC5r4nQBtnd9qgjnG8fBN5+gnqIeWEIcUFUdCG4su/vrbQ1py8XGKNUBuDjkyTv25Gd3hlrtNJV3eOKZVSL8ePAgMBAAE=" + }; + + private Peer other = new Peer + { + AgentVersion = "other", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + Addresses = new[] + { + new MultiAddress("/ip4/127.0.0.1/tcp/4001") + } + }; + + private Cid cid1 = + "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + + [TestMethod] + public void Add() + { + using (var router = new ContentRouter()) + { + router.Add(cid1, self.Id); + + var providers = router.Get(cid1); + Assert.AreEqual(1, providers.Count()); + Assert.AreEqual(self.Id, providers.First()); + } + } + + [TestMethod] + public void Add_Duplicate() + { + using (var router = new ContentRouter()) + { + router.Add(cid1, self.Id); + router.Add(cid1, self.Id); + + var providers = router.Get(cid1); + Assert.AreEqual(1, providers.Count()); + Assert.AreEqual(self.Id, providers.First()); + } + } + + [TestMethod] + public void Add_MultipleProviders() + { + using (var router = new ContentRouter()) + { + router.Add(cid1, self.Id); + router.Add(cid1, other.Id); + + var providers = router.Get(cid1).ToArray(); + Assert.AreEqual(2, providers.Length); + CollectionAssert.Contains(providers, self.Id); + CollectionAssert.Contains(providers, other.Id); + } + } + + [TestMethod] + public void Get_NonexistentCid() + { + using (var router = new ContentRouter()) + { + var providers = router.Get(cid1); + Assert.AreEqual(0, providers.Count()); + } + } + + [TestMethod] + public void Get_Expired() + { + using (var router = new ContentRouter()) + { + router.Add(cid1, self.Id, DateTime.MinValue); + + var providers = router.Get(cid1); + Assert.AreEqual(0, providers.Count()); + } + } + + [TestMethod] + public void Get_NotExpired() + { + using (var router = new ContentRouter()) + { + router.Add(cid1, self.Id, DateTime.MinValue); + var providers = router.Get(cid1); + Assert.AreEqual(0, providers.Count()); + + router.Add(cid1, self.Id, DateTime.MaxValue - router.ProviderTtl); + providers = router.Get(cid1); + Assert.AreEqual(1, providers.Count()); + } + } + } +} diff --git a/src/Lib.P2P.Tests/Routing/Dht1Test.cs b/src/Lib.P2P.Tests/Routing/Dht1Test.cs new file mode 100644 index 0000000000..aaa97c5a79 --- /dev/null +++ b/src/Lib.P2P.Tests/Routing/Dht1Test.cs @@ -0,0 +1,478 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Threading.Tasks; +using Lib.P2P.Routing; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests.Routing +{ + [TestClass] + public class Dht1Test + { + private Peer self = new Peer + { + AgentVersion = "self", + Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCC5r4nQBtnd9qgjnG8fBN5+gnqIeWEIcUFUdCG4su/vrbQ1py8XGKNUBuDjkyTv25Gd3hlrtNJV3eOKZVSL8ePAgMBAAE=" + }; + + private Peer other = new Peer + { + AgentVersion = "other", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + Addresses = new[] + { + new MultiAddress("/ip4/127.0.0.1/tcp/0") + } + }; + + [TestMethod] + public async Task StoppedEventRaised() + { + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + var stopped = false; + dht.Stopped += (s, e) => { stopped = true; }; + await dht.StartAsync(); + await dht.StopAsync(); + Assert.IsTrue(stopped); + } + + [TestMethod] + public async Task SeedsRoutingTableFromSwarm() + { + var swarm = new SwarmService(self); + var peer = swarm.RegisterPeerAddress( + "/ip4/127.0.0.1/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h"); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + try + { + Assert.IsTrue(dht.RoutingTable.Contains(peer)); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task AddDiscoveredPeerToRoutingTable() + { + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + try + { + var peer = swarm.RegisterPeerAddress( + "/ip4/127.0.0.1/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h"); + Assert.IsTrue(dht.RoutingTable.Contains(peer)); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task RemovesPeerFromRoutingTable() + { + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + try + { + var peer = swarm.RegisterPeerAddress( + "/ip4/127.0.0.1/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h"); + Assert.IsTrue(dht.RoutingTable.Contains(peer)); + + swarm.DeregisterPeer(peer); + Assert.IsFalse(dht.RoutingTable.Contains(peer)); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task ProcessFindNodeMessage_Self() + { + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + try + { + var request = new DhtMessage + { + Type = MessageType.FindNode, + Key = self.Id.ToArray() + }; + var response = dht.ProcessFindNode(request, new DhtMessage()); + Assert.AreEqual(1, response.CloserPeers.Length); + var ok = response.CloserPeers[0].TryToPeer(out var found); + Assert.IsTrue(ok); + Assert.AreEqual(self, found); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task ProcessFindNodeMessage_InRoutingTable() + { + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + try + { + dht.RoutingTable.Add(other); + var request = new DhtMessage + { + Type = MessageType.FindNode, + Key = other.Id.ToArray() + }; + var response = dht.ProcessFindNode(request, new DhtMessage()); + Assert.AreEqual(1, response.CloserPeers.Length); + var ok = response.CloserPeers[0].TryToPeer(out var found); + Assert.IsTrue(ok); + Assert.AreEqual(other, found); + CollectionAssert.AreEqual(other.Addresses.ToArray(), + found.Addresses.Select(a => a.WithoutPeerId()).ToArray()); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task ProcessFindNodeMessage_InSwarm() + { + var swarmA = new SwarmService(self); + var swarmB = swarmA.RegisterPeerAddress( + "/ip4/127.0.0.1/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h"); + var dht = new DhtService {SwarmService = swarmA}; + await dht.StartAsync(); + try + { + dht.RoutingTable.Add(swarmB); + var request = new DhtMessage + { + Type = MessageType.FindNode, + Key = swarmB.Id.ToArray() + }; + var response = dht.ProcessFindNode(request, new DhtMessage()); + Assert.AreEqual(1, response.CloserPeers.Length); + var ok = response.CloserPeers[0].TryToPeer(out var found); + Assert.IsTrue(ok); + Assert.AreEqual(swarmB, found); + CollectionAssert.AreEqual( + swarmB.Addresses.Select(a => a.WithoutPeerId()).ToArray(), + found.Addresses.Select(a => a.WithoutPeerId()).ToArray()); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task ProcessFindNodeMessage_Closest() + { + var swarm = new SwarmService(self); + swarm.RegisterPeerAddress("/ip4/127.0.0.1/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1a"); + swarm.RegisterPeerAddress("/ip4/127.0.0.2/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1b"); + swarm.RegisterPeerAddress("/ip4/127.0.0.3/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1c"); + swarm.RegisterPeerAddress("/ip4/127.0.0.4/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1d"); + swarm.RegisterPeerAddress("/ip4/127.0.0.5/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1e"); + var dht = new DhtService {SwarmService = swarm, CloserPeerCount = 3}; + await dht.StartAsync(); + try + { + dht.RoutingTable.Add(other); + var request = new DhtMessage + { + Type = MessageType.FindNode, + Key = other.Id.ToArray() + }; + var response = dht.ProcessFindNode(request, new DhtMessage()); + Assert.AreEqual(3, response.CloserPeers.Length); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task ProcessFindNodeMessage_BadNodeId() + { + var swarm = new SwarmService(self); + swarm.RegisterPeerAddress("/ip4/127.0.0.1/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1a"); + swarm.RegisterPeerAddress("/ip4/127.0.0.2/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1b"); + swarm.RegisterPeerAddress("/ip4/127.0.0.3/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1c"); + swarm.RegisterPeerAddress("/ip4/127.0.0.4/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1d"); + swarm.RegisterPeerAddress("/ip4/127.0.0.5/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1e"); + var dht = new DhtService {SwarmService = swarm, CloserPeerCount = 3}; + await dht.StartAsync(); + try + { + dht.RoutingTable.Add(other); + var request = new DhtMessage + { + Type = MessageType.FindNode, + Key = new byte[] {0xFF, 1, 2, 3} + }; + var response = dht.ProcessFindNode(request, new DhtMessage()); + Assert.AreEqual(3, response.CloserPeers.Length); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task ProcessFindNodeMessage_NoOtherPeers() + { + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + try + { + var request = new DhtMessage + { + Type = MessageType.FindNode, + Key = new MultiHash("QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h").ToArray() + }; + var response = dht.ProcessFindNode(request, new DhtMessage()); + Assert.AreEqual(0, response.CloserPeers.Length); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task ProcessGetProvidersMessage_HasCloserPeers() + { + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + try + { + dht.RoutingTable.Add(other); + Cid cid = + "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + var request = new DhtMessage + { + Type = MessageType.GetProviders, + Key = cid.Hash.ToArray() + }; + var response = dht.ProcessGetProviders(request, new DhtMessage()); + Assert.AreNotEqual(0, response.CloserPeers.Length); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task ProcessGetProvidersMessage_HasProvider() + { + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + try + { + swarm.RegisterPeer(other); + Cid cid = + "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + dht.ContentRouter.Add(cid, other.Id); + var request = new DhtMessage + { + Type = MessageType.GetProviders, + Key = cid.Hash.ToArray() + }; + var response = dht.ProcessGetProviders(request, new DhtMessage()); + Assert.AreEqual(1, response.ProviderPeers.Length); + response.ProviderPeers[0].TryToPeer(out var found); + Assert.AreEqual(other, found); + Assert.AreNotEqual(0, found.Addresses.Count()); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task ProcessAddProviderMessage() + { + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + try + { + Cid cid = + "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + var request = new DhtMessage + { + Type = MessageType.AddProvider, + Key = cid.Hash.ToArray(), + ProviderPeers = new[] + { + new DhtPeerMessage + { + Id = other.Id.ToArray(), + Addresses = other.Addresses.Select(a => a.ToArray()).ToArray() + } + } + }; + var response = dht.ProcessAddProvider(other, request, new DhtMessage()); + Assert.IsNull(response); + var providers = dht.ContentRouter.Get(cid).ToArray(); + Assert.AreEqual(1, providers.Length); + Assert.AreEqual(other.Id, providers[0]); + + var provider = swarm.KnownPeers.Single(p => p == other); + Assert.AreNotEqual(0, provider.Addresses.Count()); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task QueryIsCancelled_WhenDhtStops() + { + var unknownPeer = new MultiHash("QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCxxx"); + var swarm = new SwarmService(self); + swarm.RegisterPeerAddress( + "/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd"); + swarm.RegisterPeerAddress( + "/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64"); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + await dht.FindPeerAsync(unknownPeer); + await Task.Delay(400).ConfigureAwait(false); + await dht.StopAsync(); + } + + [TestMethod] + public async Task FindPeer_NoPeers() + { + var unknownPeer = new MultiHash("QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCxxx"); + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + + try + { + var peer = await dht.FindPeerAsync(unknownPeer); + Assert.IsNull(peer); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task FindPeer_Closest() + { + var unknownPeer = new MultiHash("QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCxxx"); + var swarm = new SwarmService(self); + await swarm.StartAsync(); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + dht.RoutingTable.Add(other); + try + { + var peer = await dht.FindPeerAsync(unknownPeer); + Assert.AreEqual(other, peer); + } + finally + { + await swarm.StopAsync(); + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task Add_FindProviders() + { + Cid cid = "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + + try + { + dht.ContentRouter.Add(cid, other.Id); + var peers = (await dht.FindProvidersAsync(cid, 1)).ToArray(); + Assert.AreEqual(1, peers.Length); + Assert.AreEqual(other, peers[0]); + } + finally + { + await dht.StopAsync(); + } + } + + [TestMethod] + public async Task Provide() + { + Cid cid = "zBunRGrmCGokA1oMESGGTfrtcMFsVA8aEtcNzM54akPWXF97uXCqTjF3GZ9v8YzxHrG66J8QhtPFWwZebRZ2zeUEELu67"; + var swarm = new SwarmService(self); + var dht = new DhtService {SwarmService = swarm}; + await dht.StartAsync(); + + try + { + await swarm.StartAsync(); + await swarm.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + await dht.ProvideAsync(cid); + var peers = (await dht.FindProvidersAsync(cid, 1)).ToArray(); + Assert.AreEqual(1, peers.Length); + Assert.AreEqual(self, peers[0]); + } + finally + { + await dht.StopAsync(); + await swarm.StopAsync(); + } + } + } +} diff --git a/src/Lib.P2P.Tests/Routing/DistributedQueryTests.cs b/src/Lib.P2P.Tests/Routing/DistributedQueryTests.cs new file mode 100644 index 0000000000..a7c5198217 --- /dev/null +++ b/src/Lib.P2P.Tests/Routing/DistributedQueryTests.cs @@ -0,0 +1,56 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Routing; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.Routing +{ + [TestClass] + public class DistributedQueryTest + { + [TestMethod] + public async Task Cancelling() + { + var dquery = new DistributedQuery + { + Dht = new DhtService() + }; + var cts = new CancellationTokenSource(); + cts.Cancel(); + await dquery.RunAsync(cts.Token); + Assert.AreEqual(0, dquery.Answers.Count()); + } + + [TestMethod] + public void UniqueId() + { + var q1 = new DistributedQuery(); + var q2 = new DistributedQuery(); + Assert.AreNotEqual(q1.Id, q2.Id); + } + } +} diff --git a/src/Lib.P2P.Tests/SecureCommunication/Psk1ProtectorTest.cs b/src/Lib.P2P.Tests/SecureCommunication/Psk1ProtectorTest.cs new file mode 100644 index 0000000000..38162ead91 --- /dev/null +++ b/src/Lib.P2P.Tests/SecureCommunication/Psk1ProtectorTest.cs @@ -0,0 +1,45 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading.Tasks; +using Lib.P2P.Cryptography; +using Lib.P2P.SecureCommunication; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.SecureCommunication +{ + [TestClass] + public class Psk1ProtectorTest + { + [TestMethod] + public async Task Protect() + { + var psk = new PreSharedKey().Generate(); + var protector = new Psk1Protector {Key = psk}; + var connection = new PeerConnection {Stream = Stream.Null}; + var protectedStream = await protector.ProtectAsync(connection); + Assert.IsInstanceOfType(protectedStream, typeof(Psk1Stream)); + } + } +} diff --git a/src/Lib.P2P.Tests/SecureCommunication/Psk1StreamTest.cs b/src/Lib.P2P.Tests/SecureCommunication/Psk1StreamTest.cs new file mode 100644 index 0000000000..a97358d961 --- /dev/null +++ b/src/Lib.P2P.Tests/SecureCommunication/Psk1StreamTest.cs @@ -0,0 +1,97 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using Lib.P2P.Cryptography; +using Lib.P2P.SecureCommunication; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests.SecureCommunication +{ + [TestClass] + public class Psk1StreamTest + { + [TestMethod] + [ExpectedException(typeof(Exception))] + public void BadKeyLength() + { + var psk = new PreSharedKey(); + var _ = new Psk1Stream(Stream.Null, psk); + } + + [TestMethod] + public void FirstWriteSendsNonce() + { + var psk = new PreSharedKey().Generate(); + + var insecure = new MemoryStream(); + var secure = new Psk1Stream(insecure, psk); + secure.WriteByte(0x10); + Assert.AreEqual(24 + 1, insecure.Length); + + insecure = new MemoryStream(); + secure = new Psk1Stream(insecure, psk); + secure.Write(new byte[10], 0, 10); + Assert.AreEqual(24 + 10, insecure.Length); + + insecure = new MemoryStream(); + secure = new Psk1Stream(insecure, psk); + secure.WriteAsync(new byte[12], 0, 12).Wait(); + Assert.AreEqual(24 + 12, insecure.Length); + } + + [TestMethod] + public void Roundtrip() + { + var psk = new PreSharedKey().Generate(); + var plain = new byte[] {1, 2, 3}; + var plain1 = new byte[3]; + var plain2 = new byte[3]; + + var insecure = new MemoryStream(); + var secure = new Psk1Stream(insecure, psk); + secure.Write(plain, 0, plain.Length); + secure.Flush(); + + insecure.Position = 0; + secure = new Psk1Stream(insecure, psk); + secure.Read(plain1, 0, plain1.Length); + CollectionAssert.AreEqual(plain, plain1); + + insecure.Position = 0; + secure = new Psk1Stream(insecure, psk); + secure.ReadAsync(plain2, 0, plain2.Length).Wait(); + CollectionAssert.AreEqual(plain, plain2); + } + + [TestMethod] + [ExpectedException(typeof(EndOfStreamException))] + public void ReadingInvalidNonce() + { + var psk = new PreSharedKey().Generate(); + var secure = new Psk1Stream(Stream.Null, psk); + secure.ReadByte(); + } + } +} diff --git a/src/Lib.P2P.Tests/StreamExtensionsTest.cs b/src/Lib.P2P.Tests/StreamExtensionsTest.cs new file mode 100644 index 0000000000..88c0f582ff --- /dev/null +++ b/src/Lib.P2P.Tests/StreamExtensionsTest.cs @@ -0,0 +1,95 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests +{ + [TestClass] + public class StreamExtensionsTest + { + [TestMethod] + public async Task ReadAsync() + { + var expected = new byte[] {1, 2, 3, 4}; + await using (var ms = new MemoryStream(expected)) + { + var actual = new byte[expected.Length]; + await ms.ReadExactAsync(actual, 0, actual.Length); + CollectionAssert.AreEqual(expected, actual); + } + } + + [TestMethod] + public void ReadAsync_EOS() + { + var expected = new byte[] {1, 2, 3, 4}; + var actual = new byte[expected.Length + 1]; + + using (var ms = new MemoryStream(expected)) + { + ExceptionAssert.Throws(() => + { + ms.ReadExactAsync(actual, 0, actual.Length).Wait(); + }); + } + + var cancel = new CancellationTokenSource(); + using (var ms = new MemoryStream(expected)) + { + ExceptionAssert.Throws(() => + { + ms.ReadExactAsync(actual, 0, actual.Length, cancel.Token).Wait(cancel.Token); + }); + } + } + + [TestMethod] + public async Task ReadAsync_Cancel() + { + var expected = new byte[] {1, 2, 3, 4}; + var actual = new byte[expected.Length]; + var cancel = new CancellationTokenSource(); + await using (var ms = new MemoryStream(expected)) + { + await ms.ReadExactAsync(actual, 0, actual.Length, cancel.Token); + CollectionAssert.AreEqual(expected, actual); + } + + cancel.Cancel(); + await using (var ms = new MemoryStream(expected)) + { + ExceptionAssert.Throws(() => + { + if (ms != null) + { + ms.ReadExactAsync(actual, 0, actual.Length, cancel.Token).Wait(); + } + }); + } + } + } +} diff --git a/src/Lib.P2P.Tests/SwarmTest.cs b/src/Lib.P2P.Tests/SwarmTest.cs new file mode 100644 index 0000000000..a9ea2a6597 --- /dev/null +++ b/src/Lib.P2P.Tests/SwarmTest.cs @@ -0,0 +1,1277 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests +{ + [TestClass] + public sealed class SwarmTest + { + private readonly MultiAddress _mars = + "/ip4/10.1.10.10/tcp/29087/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; + + private readonly MultiAddress _venus = + "/ip4/104.236.76.40/tcp/4001/ipfs/QmSoLV4Bbm51jM9C4gDYZQ9Cy3U6aXMJDAbzgu2fzaDs64"; + + private readonly MultiAddress _earth = + "/ip4/178.62.158.247/tcp/4001/ipfs/QmSoLer265NRgSp2LA3dPaeykiS1J6DifTC88f5uVQKNAd"; + + private readonly Peer _self = new Peer + { + AgentVersion = "self", + Id = "QmXK9VBxaXFuuT29AaPUTgW3jBWZ9JgLVZYdMYTHC6LLAH", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQCC5r4nQBtnd9qgjnG8fBN5+gnqIeWEIcUFUdCG4su/vrbQ1py8XGKNUBuDjkyTv25Gd3hlrtNJV3eOKZVSL8ePAgMBAAE=" + }; + + private readonly Peer _other = new Peer + { + AgentVersion = "other", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + + [TestMethod] + public async Task Start_Stop() + { + var swarm = new SwarmService(_self); + await swarm.StartAsync(); + await swarm.StopAsync(); + } + + [TestMethod] + public void Start_NoLocalPeer() + { + var swarm = new SwarmService(null); + ExceptionAssert.Throws(() => + { + swarm.StartAsync().Wait(); + }); + } + + [TestMethod] + public void NewPeerAddress() + { + var swarm = new SwarmService(_self); + swarm.RegisterPeerAddress(_mars); + Assert.IsTrue(swarm.KnownPeerAddresses.Contains(_mars)); + } + + [TestMethod] + public void NewPeerAddress_Self() + { + var swarm = new SwarmService(_self); + var selfAddress = "/ip4/178.62.158.247/tcp/4001/ipfs/" + _self.Id; + ExceptionAssert.Throws(() => + { + var _ = swarm.RegisterPeerAddress(selfAddress); + }); + + selfAddress = "/ip4/178.62.158.247/tcp/4001/p2p/" + _self.Id; + ExceptionAssert.Throws(() => + { + var _ = swarm.RegisterPeerAddress(selfAddress); + }); + } + + //turn off blacklisting + //[TestMethod] + //public void NewPeerAddress_BlackList() + //{ + // var swarm = new SwarmService(_self); + // swarm.BlackList.Add(_mars); + + // ExceptionAssert.Throws(() => + // { + // var _ = swarm.RegisterPeerAddress(_mars); + // }); + // Assert.IsFalse(swarm.KnownPeerAddresses.Contains(_mars)); + + // Assert.IsNotNull(swarm.RegisterPeerAddress(_venus)); + // Assert.IsTrue(swarm.KnownPeerAddresses.Contains(_venus)); + //} + + //turn off blacklisting + //[TestMethod] + //public void NewPeerAddress_WhiteList() + //{ + // var swarm = new SwarmService(_self); + // swarm.WhiteList.Add(_venus); + + // ExceptionAssert.Throws(() => + // { + // var _ = swarm.RegisterPeerAddress(_mars); + // }); + // Assert.IsFalse(swarm.KnownPeerAddresses.Contains(_mars)); + + // Assert.IsNotNull(swarm.RegisterPeerAddress(_venus)); + // Assert.IsTrue(swarm.KnownPeerAddresses.Contains(_venus)); + //} + + [TestMethod] + public void NewPeerAddress_InvalidAddress_MissingPeerId() + { + var swarm = new SwarmService(_self); + ExceptionAssert.Throws(() => + { + var _ = swarm.RegisterPeerAddress("/ip4/10.1.10.10/tcp/29087"); + }); + Assert.AreEqual(0, swarm.KnownPeerAddresses.Count()); + } + + [TestMethod] + public void NewPeerAddress_Duplicate() + { + var swarm = new SwarmService(_self); + swarm.RegisterPeerAddress(_mars); + Assert.AreEqual(1, swarm.KnownPeerAddresses.Count()); + + swarm.RegisterPeerAddress(_mars); + Assert.AreEqual(1, swarm.KnownPeerAddresses.Count()); + } + + [TestMethod] + public void KnownPeers() + { + var swarm = new SwarmService(_self); + Assert.AreEqual(0, swarm.KnownPeers.Count()); + Assert.AreEqual(0, swarm.KnownPeerAddresses.Count()); + + swarm.RegisterPeerAddress("/ip4/10.1.10.10/tcp/29087/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"); + Assert.AreEqual(1, swarm.KnownPeers.Count()); + Assert.AreEqual(1, swarm.KnownPeerAddresses.Count()); + + swarm.RegisterPeerAddress("/ip4/10.1.10.11/tcp/29087/p2p/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"); + Assert.AreEqual(1, swarm.KnownPeers.Count()); + Assert.AreEqual(2, swarm.KnownPeerAddresses.Count()); + + swarm.RegisterPeerAddress(_venus); + Assert.AreEqual(2, swarm.KnownPeers.Count()); + Assert.AreEqual(3, swarm.KnownPeerAddresses.Count()); + } + + [TestMethod] + public async Task Connect_Disconnect() + { + var peerB = new Peer + { + AgentVersion = "peerB", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + var swarmB = new SwarmService(peerB); + await swarmB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + Assert.IsTrue(peerB.Addresses.Any()); + + var swarm = new SwarmService(_self); + await swarm.StartAsync(); + try + { + var remotePeer = (await swarm.ConnectAsync(peerBAddress)).RemotePeer; + Assert.IsNotNull(remotePeer.ConnectedAddress); + Assert.AreEqual(peerB.PublicKey, remotePeer.PublicKey); + Assert.IsTrue(remotePeer.IsValid()); + Assert.IsTrue(swarm.KnownPeers.Contains(peerB)); + + // wait for swarmB to settle + var endTime = DateTime.Now.AddSeconds(3); + while (true) + { + if (DateTime.Now > endTime) + { + Assert.Fail("swarmB does not know about self"); + } + + if (swarmB.KnownPeers.Contains(_self)) + { + break; + } + + await Task.Delay(100); + } + + var me = swarmB.KnownPeers.First(p => p == _self); + Assert.AreEqual(_self.Id, me.Id); + Assert.AreEqual(_self.PublicKey, me.PublicKey); + Assert.IsNotNull(me.ConnectedAddress); + + // Check disconnect + await swarm.DisconnectAsync(peerBAddress); + Assert.IsNull(remotePeer.ConnectedAddress); + Assert.IsTrue(swarm.KnownPeers.Contains(peerB)); + Assert.IsTrue(swarmB.KnownPeers.Contains(_self)); + + // wait for swarmB to settle + endTime = DateTime.Now.AddSeconds(3); + while (true) + { + if (DateTime.Now > endTime) + { + Assert.Fail("swarmB did not close connection."); + } + + if (me.ConnectedAddress == null) + { + break; + } + + await Task.Delay(100); + } + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public async Task Connect_Disconnect_Reconnect() + { + var peerB = new Peer + { + AgentVersion = "peerB", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + var swarmB = new SwarmService(peerB); + await swarmB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + Assert.IsTrue(peerB.Addresses.Any()); + + var swarm = new SwarmService(_self); + await swarm.StartAsync(); + await swarm.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + try + { + var remotePeer = (await swarm.ConnectAsync(peerBAddress)).RemotePeer; + Assert.IsNotNull(remotePeer.ConnectedAddress); + Assert.AreEqual(peerB.PublicKey, remotePeer.PublicKey); + Assert.IsTrue(remotePeer.IsValid()); + Assert.IsTrue(swarm.KnownPeers.Contains(peerB)); + + // wait for swarmB to settle + var endTime = DateTime.Now.AddSeconds(3); + while (true) + { + if (DateTime.Now > endTime) + { + Assert.Fail("swarmB does not know about self"); + } + + if (swarmB.KnownPeers.Contains(_self)) + { + break; + } + + await Task.Delay(100); + } + + var me = swarmB.KnownPeers.First(p => p == _self); + Assert.AreEqual(_self.Id, me.Id); + Assert.AreEqual(_self.PublicKey, me.PublicKey); + Assert.IsNotNull(me.ConnectedAddress); + + // Check disconnect + await swarm.DisconnectAsync(peerBAddress); + Assert.IsNull(remotePeer.ConnectedAddress); + Assert.IsTrue(swarm.KnownPeers.Contains(peerB)); + Assert.IsTrue(swarmB.KnownPeers.Contains(_self)); + + // wait for swarmB to settle + endTime = DateTime.Now.AddSeconds(3); + while (true) + { + if (DateTime.Now > endTime) + { + Assert.Fail("swarmB did not close connection."); + } + + if (me.ConnectedAddress == null) + { + break; + } + + await Task.Delay(100); + } + + // Reconnect + remotePeer = (await swarm.ConnectAsync(peerBAddress)).RemotePeer; + Assert.IsNotNull(remotePeer.ConnectedAddress); + Assert.AreEqual(peerB.PublicKey, remotePeer.PublicKey); + Assert.IsTrue(remotePeer.IsValid()); + Assert.IsTrue(swarm.KnownPeers.Contains(peerB)); + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public async Task RemotePeer_Contains_ConnectedAddress1() + { + var peerB = new Peer + { + AgentVersion = "peerB", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + var swarmB = new SwarmService(peerB); + await swarmB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/0.0.0.0/tcp/0"); + + var swarm = new SwarmService(_self); + await swarm.StartAsync(); + try + { + var connection = await swarm.ConnectAsync(peerBAddress); + var remote = connection.RemotePeer; + Assert.AreEqual(remote.ConnectedAddress, peerBAddress); + CollectionAssert.Contains(remote.Addresses.ToArray(), peerBAddress); + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public async Task RemotePeer_Contains_ConnectedAddress2() + { + // Only works on Windows because connecting to 127.0.0.100 is allowed + // when listening on 0.0.0.0 + if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return; + + var peerB = new Peer + { + AgentVersion = "peerB", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + var swarmB = new SwarmService(peerB); + await swarmB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/0.0.0.0/tcp/0"); + var peerBPort = peerBAddress.Protocols[1].Value; + Assert.IsTrue(peerB.Addresses.Any()); + + var swarm = new SwarmService(_self); + await swarm.StartAsync(); + try + { + MultiAddress ma = $"/ip4/127.0.0.100/tcp/{peerBPort}/ipfs/{peerB.Id}"; + var connection = await swarm.ConnectAsync(ma); + var remote = connection.RemotePeer; + Assert.AreEqual(remote.ConnectedAddress, ma); + CollectionAssert.Contains(remote.Addresses.ToArray(), ma); + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public async Task Connect_CancelsOnStop() + { + var swarm = new SwarmService(_self); + var venus = new Peer + { + Id = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + Addresses = new MultiAddress[] + { + "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io + } + }; + + await swarm.StartAsync(); + var a = swarm.ConnectAsync(venus); + Assert.IsFalse(a.IsCanceled || a.IsFaulted); + + await swarm.StopAsync(); + var endTime = DateTime.Now.AddSeconds(3); + while (!a.IsCanceled && !a.IsFaulted) + { + if (DateTime.Now > endTime) + { + Assert.Fail("swarm did not cancel pending connection."); + } + + await Task.Delay(100); + } + + Assert.IsTrue(a.IsCanceled || a.IsFaulted); + } + + [TestMethod] + public async Task Connect_IsPending() + { + var swarm = new SwarmService(_self); + var venus = new Peer + { + Id = "QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", + Addresses = new MultiAddress[] + { + "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ", // mars.i.ipfs.io + } + }; + + await swarm.StartAsync(); + try + { + Assert.IsFalse(swarm.HasPendingConnection(venus)); + + var _ = swarm.ConnectAsync(venus); + Assert.IsTrue(swarm.HasPendingConnection(venus)); + } + finally + { + await swarm.StopAsync(); + } + } + + [TestMethod] + public async Task Connect_WithSomeUnreachableAddresses() + { + const string bid = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h"; + var peerB = new Peer + { + AgentVersion = "peerB", + Id = bid, + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=", + Addresses = new MultiAddress[] + { + $"/ip4/127.0.0.2/tcp/2/ipfs/{bid}", + $"/ip4/127.0.0.3/tcp/3/ipfs/{bid}" + } + }; + var swarmB = new SwarmService(peerB); + await swarmB.StartAsync(); + var _ = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + Assert.IsTrue(peerB.Addresses.Any()); + + var swarm = new SwarmService(_self); + await swarm.StartAsync(); + try + { + var remotePeer = (await swarm.ConnectAsync(peerB)).RemotePeer; + Assert.IsNotNull(remotePeer.ConnectedAddress); + Assert.AreEqual(peerB.PublicKey, remotePeer.PublicKey); + Assert.IsTrue(remotePeer.IsValid()); + Assert.IsTrue(swarm.KnownPeers.Contains(peerB)); + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public async Task ConnectionEstablished() + { + var peerB = new Peer + { + AgentVersion = "peerB", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + var swarmB = new SwarmService(peerB); + var swarmBConnections = 0; + swarmB.ConnectionEstablished += (s, e) => { ++swarmBConnections; }; + await swarmB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var swarm = new SwarmService(_self); + var swarmConnections = 0; + swarm.ConnectionEstablished += (s, e) => { ++swarmConnections; }; + await swarm.StartAsync(); + try + { + var _ = await swarm.ConnectAsync(peerBAddress); + Assert.AreEqual(1, swarmConnections); + + // wait for swarmB to settle + var endTime = DateTime.Now.AddSeconds(3); + while (true) + { + if (DateTime.Now > endTime) + { + Assert.Fail("swarmB did not raise event."); + } + + if (swarmBConnections == 1) + { + break; + } + + await Task.Delay(100).ConfigureAwait(false); + } + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public void Connect_No_Transport() + { + const string remoteId = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb"; + MultiAddress remoteAddress = $"/ip4/127.0.0.1/ipfs/{remoteId}"; + var swarm = new SwarmService(_self); + swarm.StartAsync().Wait(); + try + { + ExceptionAssert.Throws(() => + { + var _ = swarm.ConnectAsync(remoteAddress).Result; + }); + } + finally + { + swarm.StopAsync().Wait(); + } + } + + [TestMethod] + public void Connect_Refused() + { + const string remoteId = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb"; + MultiAddress remoteAddress = $"/ip4/127.0.0.1/tcp/4040/ipfs/{remoteId}"; + var swarm = new SwarmService(_self); + swarm.StartAsync().Wait(); + try + { + ExceptionAssert.Throws(() => + { + var _ = swarm.ConnectAsync(remoteAddress).Result; + }); + } + finally + { + swarm.StopAsync().Wait(); + } + } + + [TestMethod] + public void Connect_Failure_Event() + { + const string remoteId = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb"; + MultiAddress remoteAddress = $"/ip4/127.0.0.1/tcp/4040/ipfs/{remoteId}"; + var swarm = new SwarmService(_self); + Peer unreachable = null; + swarm.PeerNotReachable += (s, e) => { unreachable = e; }; + swarm.StartAsync().Wait(); + try + { + ExceptionAssert.Throws(() => + { + var _ = swarm.ConnectAsync(remoteAddress).Result; + }); + } + finally + { + swarm.StopAsync().Wait(); + } + + Assert.IsNotNull(unreachable); + Assert.AreEqual(remoteId, unreachable.Id.ToBase58()); + } + + [TestMethod] + public void Connect_Not_Peer() + { + const string remoteId = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb"; + MultiAddress remoteAddress = $"/dns/npmjs.com/tcp/80/ipfs/{remoteId}"; + var swarm = new SwarmService(_self); + swarm.StartAsync().Wait(); + try + { + ExceptionAssert.Throws(() => + { + var _ = swarm.ConnectAsync(remoteAddress).Result; + }); + } + finally + { + swarm.StopAsync().Wait(); + } + } + + [TestMethod] + public void Connect_Cancelled() + { + var cs = new CancellationTokenSource(); + cs.Cancel(); + const string remoteId = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb"; + MultiAddress remoteAddress = $"/ip4/127.0.0.1/tcp/4002/ipfs/{remoteId}"; + var swarm = new SwarmService(_self); + swarm.StartAsync().Wait(cs.Token); + try + { + ExceptionAssert.Throws(() => + { + var _ = swarm.ConnectAsync(remoteAddress, cs.Token).Result; + }); + } + finally + { + swarm.StopAsync().Wait(cs.Token); + } + } + + [TestMethod] + public void Connecting_To_Blacklisted_Address() + { + var swarm = new SwarmService(_self); + swarm.BlackList.Add(_mars); + swarm.StartAsync().Wait(); + try + { + ExceptionAssert.Throws(() => + { + var _ = swarm.ConnectAsync(_mars).Result; + }); + } + finally + { + swarm.StopAsync().Wait(); + } + } + + [TestMethod] + public void Connecting_To_Self() + { + var swarm = new SwarmService(_self); + swarm.StartAsync().Wait(); + try + { + ExceptionAssert.Throws(() => + { + var _ = swarm.ConnectAsync(_earth).Result; + }); + } + finally + { + swarm.StopAsync().Wait(); + } + } + + [TestMethod] + public async Task Connecting_To_Self_Indirect() + { + var swarm = new SwarmService(_self); + await swarm.StartAsync(); + try + { + var listen = await swarm.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + var bad = listen.Clone(); + bad.Protocols[2].Value = "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb"; + ExceptionAssert.Throws(() => { swarm.ConnectAsync(bad).Wait(); }); + } + finally + { + await swarm.StopAsync(); + } + } + + [TestMethod] + public async Task PeerDisconnected() + { + var peerB = new Peer + { + AgentVersion = "peerB", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + var swarmB = new SwarmService(peerB); + await swarmB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var swarm = new SwarmService(_self); + var swarmConnections = 0; + swarm.ConnectionEstablished += (s, e) => { ++swarmConnections; }; + swarm.PeerDisconnected += (s, e) => { --swarmConnections; }; + await swarm.StartAsync(); + try + { + var _ = await swarm.ConnectAsync(peerBAddress); + Assert.AreEqual(1, swarmConnections); + + await swarm.StopAsync(); + Assert.AreEqual(0, swarmConnections); + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public async Task Listening() + { + var peerA = new Peer + { + Id = _self.Id, + PublicKey = _self.PublicKey, + AgentVersion = _self.AgentVersion + }; + MultiAddress addr = "/ip4/127.0.0.1/tcp/0"; + var swarmA = new SwarmService(peerA); + var peerB = new Peer + { + Id = _other.Id, + PublicKey = _other.PublicKey, + AgentVersion = _other.AgentVersion + }; + var swarmB = new SwarmService(peerB); + await swarmA.StartAsync(); + await swarmB.StartAsync(); + try + { + var another = await swarmA.StartListeningAsync(addr); + Assert.IsTrue(peerA.Addresses.Contains(another)); + + await swarmB.ConnectAsync(another); + Assert.IsTrue(swarmB.KnownPeers.Contains(peerA)); + + await swarmA.StopListeningAsync(addr); + Assert.AreEqual(0, peerA.Addresses.Count()); + } + finally + { + await swarmA.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public async Task Listening_Start_Stop() + { + var peer = new Peer + { + Id = _self.Id, + PublicKey = _self.PublicKey, + AgentVersion = _self.AgentVersion + }; + MultiAddress addr = "/ip4/0.0.0.0/tcp/0"; + var swarm = new SwarmService(peer); + await swarm.StartAsync(); + + try + { + await swarm.StartListeningAsync(addr); + Assert.IsTrue(peer.Addresses.Any()); + + await swarm.StopListeningAsync(addr); + Assert.AreEqual(0, peer.Addresses.Count()); + + await swarm.StartListeningAsync(addr); + Assert.IsTrue(peer.Addresses.Any()); + + await swarm.StopListeningAsync(addr); + Assert.AreEqual(0, peer.Addresses.Count()); + } + finally + { + await swarm.StopAsync(); + } + } + + [TestMethod] + public async Task Stop_Closes_Listeners() + { + var peer = new Peer + { + Id = _self.Id, + PublicKey = _self.PublicKey, + AgentVersion = _self.AgentVersion + }; + MultiAddress addr = "/ip4/0.0.0.0/tcp/0"; + var swarm = new SwarmService(peer); + + try + { + await swarm.StartAsync(); + await swarm.StartListeningAsync(addr); + Assert.IsTrue(peer.Addresses.Any()); + await swarm.StopAsync(); + Assert.AreEqual(0, peer.Addresses.Count()); + + await swarm.StartAsync(); + await swarm.StartListeningAsync(addr); + Assert.IsTrue(peer.Addresses.Any()); + await swarm.StopAsync(); + Assert.AreEqual(0, peer.Addresses.Count()); + } + catch (Exception) + { + await swarm.StopAsync(); + throw; + } + } + + [TestMethod] + public async Task Listening_Event() + { + var peer = new Peer + { + Id = _self.Id, + PublicKey = _self.PublicKey, + AgentVersion = _self.AgentVersion + }; + MultiAddress addr = "/ip4/127.0.0.1/tcp/0"; + var swarm = new SwarmService(peer); + Peer listeningPeer = null; + swarm.ListenerEstablished += (s, e) => { listeningPeer = e; }; + try + { + await swarm.StartListeningAsync(addr); + Assert.AreEqual(peer, listeningPeer); + Assert.AreNotEqual(0, peer.Addresses.Count()); + } + finally + { + await swarm.StopAsync(); + } + } + + [TestMethod] + public async Task Listening_AnyPort() + { + var peerA = new Peer + { + Id = _self.Id, + PublicKey = _self.PublicKey, + AgentVersion = _self.AgentVersion + }; + MultiAddress addr = "/ip4/127.0.0.1/tcp/0"; + var swarmA = new SwarmService(peerA); + var peerB = new Peer + { + Id = _other.Id, + PublicKey = _other.PublicKey, + AgentVersion = _other.AgentVersion + }; + var swarmB = new SwarmService(peerB); + await swarmA.StartAsync(); + await swarmB.StartAsync(); + try + { + var another = await swarmA.StartListeningAsync(addr); + Assert.IsTrue(peerA.Addresses.Contains(another)); + + await swarmB.ConnectAsync(another); + Assert.IsTrue(swarmB.KnownPeers.Contains(peerA)); + + // TODO: Assert.IsTrue(swarmA.KnownPeers.Contains(peerB)); + + await swarmA.StopListeningAsync(addr); + Assert.IsFalse(peerA.Addresses.Contains(another)); + } + finally + { + await swarmA.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public async Task Listening_IPv4Any() + { + var peerA = new Peer + { + Id = _self.Id, + PublicKey = _self.PublicKey, + AgentVersion = _self.AgentVersion + }; + MultiAddress addr = "/ip4/0.0.0.0/tcp/0"; + var swarmA = new SwarmService(peerA); + var peerB = new Peer + { + Id = _other.Id, + PublicKey = _other.PublicKey, + AgentVersion = _other.AgentVersion + }; + var swarmB = new SwarmService(peerB); + await swarmA.StartAsync(); + await swarmB.StartAsync(); + try + { + var another = await swarmA.StartListeningAsync(addr); + Assert.IsFalse(peerA.Addresses.Contains(addr)); + Assert.IsTrue(peerA.Addresses.Contains(another)); + + await swarmB.ConnectAsync(another); + Assert.IsTrue(swarmB.KnownPeers.Contains(peerA)); + + // TODO: Assert.IsTrue(swarmA.KnownPeers.Contains(peerB)); + + await swarmA.StopListeningAsync(addr); + Assert.AreEqual(0, peerA.Addresses.Count()); + } + finally + { + await swarmA.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + [TestCategory("IPv6")] + public async Task Listening_IPv6Any() + { + var peerA = new Peer + { + Id = _self.Id, + PublicKey = _self.PublicKey, + AgentVersion = _self.AgentVersion + }; + MultiAddress addr = "/ip6/::/tcp/0"; + var swarmA = new SwarmService(peerA); + var peerB = new Peer + { + Id = _other.Id, + PublicKey = _other.PublicKey, + AgentVersion = _other.AgentVersion + }; + var swarmB = new SwarmService(peerB); + await swarmA.StartAsync(); + await swarmB.StartAsync(); + try + { + var another = await swarmA.StartListeningAsync(addr); + Assert.IsFalse(peerA.Addresses.Contains(addr)); + Assert.IsTrue(peerA.Addresses.Contains(another)); + + await swarmB.ConnectAsync(another); + Assert.IsTrue(swarmB.KnownPeers.Contains(peerA)); + + // TODO: Assert.IsTrue(swarmA.KnownPeers.Contains(peerB)); + + await swarmA.StopListeningAsync(addr); + Assert.AreEqual(0, peerA.Addresses.Count()); + } + finally + { + await swarmA.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public void Listening_MissingTransport() + { + var peer = new Peer + { + Id = _self.Id, + PublicKey = _self.PublicKey, + AgentVersion = _self.AgentVersion + }; + var swarm = new SwarmService(peer); + ExceptionAssert.Throws(() => + { + var _ = swarm.StartListeningAsync("/ip4/127.0.0.1").Result; + }); + Assert.AreEqual(0, peer.Addresses.Count()); + } + + [TestMethod] + public void LocalPeer() + { + var swarm = new SwarmService(_self); + Assert.AreEqual(_self, swarm.LocalPeer) + ; + ExceptionAssert.Throws(() => { swarm.LocalPeer = null; }); + ExceptionAssert.Throws(() => { swarm.LocalPeer = new Peer { Id = _self.Id }; }); + ExceptionAssert.Throws(() => + { + swarm.LocalPeer = new Peer { PublicKey = _self.PublicKey }; + }); + ExceptionAssert.Throws(() => + { + swarm.LocalPeer = new Peer { Id = _self.Id, PublicKey = _other.PublicKey }; + }); + + swarm.LocalPeer = new Peer { Id = _other.Id, PublicKey = _other.PublicKey }; + Assert.AreEqual(_other, swarm.LocalPeer); + } + + [TestMethod] + public async Task Dial_Peer_No_Address() + { + var peer = new Peer + { + Id = _mars.PeerId + }; + + var swarm = new SwarmService(_self); + await swarm.StartAsync(); + try + { + ExceptionAssert.Throws(() => { swarm.DialAsync(peer, "/foo/0.42.0").Wait(); }); + } + finally + { + await swarm.StopAsync(); + } + } + + [TestMethod] + public async Task Dial_Peer_Not_Listening() + { + var peer = new Peer + { + Id = _mars.PeerId, + Addresses = new List + { + new MultiAddress($"/ip4/127.0.0.1/tcp/4242/ipfs/{_mars.PeerId}"), + new MultiAddress($"/ip4/127.0.0.2/tcp/4242/ipfs/{_mars.PeerId}") + } + }; + + var swarm = new SwarmService(_self); + await swarm.StartAsync(); + try + { + ExceptionAssert.Throws(() => { swarm.DialAsync(peer, "/foo/0.42.0").Wait(); }); + } + finally + { + await swarm.StopAsync(); + } + } + + [TestMethod] + public async Task Dial_Peer_UnknownProtocol() + { + var peerB = new Peer + { + AgentVersion = "peerB", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + var swarmB = new SwarmService(peerB); + var _ = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var swarm = new SwarmService(_self); + await swarm.StartAsync(); + try + { + ExceptionAssert.Throws(() => { swarm.DialAsync(peerB, "/foo/0.42.0").Wait(); }); + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public async Task Dial_Peer() + { + var peerB = new Peer + { + AgentVersion = "peerB", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + var swarmB = new SwarmService(peerB); + await swarmB.StartAsync(); + var _ = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + + var swarm = new SwarmService(_self); + await swarm.StartAsync(); + try + { + await using (var stream = await swarm.DialAsync(peerB, "/ipfs/id/1.0.0")) + { + Assert.IsNotNull(stream); + Assert.IsTrue(stream.CanRead); + Assert.IsTrue(stream.CanWrite); + } + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public void PeerDiscovered() + { + var swarm = new SwarmService(_self); + var peerCount = 0; + swarm.PeerDiscovered += (s, e) => { ++peerCount; }; + swarm.RegisterPeerAddress("/ip4/127.0.0.1/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h"); + swarm.RegisterPeerAddress("/ip4/127.0.0.2/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h"); + swarm.RegisterPeerAddress("/ip4/127.0.0.3/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h"); + swarm.RegisterPeerAddress("/ip4/127.0.0.1/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1i"); + swarm.RegisterPeerAddress("/ip4/127.0.0.2/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1i"); + swarm.RegisterPeerAddress("/ip4/127.0.0.3/tcp/4001/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1i"); + swarm.RegisterPeer(new Peer { Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1j" }); + swarm.RegisterPeer(new Peer { Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1j" }); + + Assert.AreEqual(3, peerCount); + } + + [TestMethod] + public async Task IsRunning() + { + var swarm = new SwarmService(_self); + Assert.IsFalse(swarm.IsRunning); + + await swarm.StartAsync(); + Assert.IsTrue(swarm.IsRunning); + + await swarm.StopAsync(); + Assert.IsFalse(swarm.IsRunning); + } + + [TestMethod] + public async Task Connect_PrivateNetwork() + { + var peerB = new Peer + { + AgentVersion = "peerB", + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + PublicKey = + "CAASXjBcMA0GCSqGSIb3DQEBAQUAA0sAMEgCQQDlTSgVLprWaXfmxDr92DJE1FP0wOexhulPqXSTsNh5ot6j+UiuMgwb0shSPKzLx9AuTolCGhnwpTBYHVhFoBErAgMBAAE=" + }; + var swarmB = new SwarmService(peerB) { NetworkProtector = new OpenNetwork() }; + await swarmB.StartAsync(); + var peerBAddress = await swarmB.StartListeningAsync("/ip4/127.0.0.1/tcp/0"); + Assert.IsTrue(peerB.Addresses.Any()); + + var swarm = new SwarmService(_self) { NetworkProtector = new OpenNetwork() }; + await swarm.StartAsync(); + try + { + var _ = await swarm.ConnectAsync(peerBAddress); + Assert.AreEqual(2, OpenNetwork.Count); + } + finally + { + await swarm.StopAsync(); + await swarmB.StopAsync(); + } + } + + [TestMethod] + public void DeregisterPeer() + { + var swarm = new SwarmService(_self); + swarm.RegisterPeer(_other); + Assert.IsTrue(swarm.KnownPeers.Contains(_other)); + + Peer removedPeer = null; + swarm.PeerRemoved += (s, e) => removedPeer = e; + swarm.DeregisterPeer(_other); + Assert.IsFalse(swarm.KnownPeers.Contains(_other)); + Assert.AreEqual(_other, removedPeer); + } + + [TestMethod] + public void IsAllowed_Peer() + { + var swarm = new SwarmService(null); + var peer = new Peer + { + Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + Addresses = new MultiAddress[] + { + "/ip4/127.0.0.1/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h" + } + }; + + Assert.IsTrue(swarm.IsAllowed(peer)); + + swarm.BlackList.Add(peer.Addresses.First()); + Assert.IsFalse(swarm.IsAllowed(peer)); + + swarm.BlackList.Clear(); + swarm.BlackList.Add("/p2p/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h"); + Assert.IsFalse(swarm.IsAllowed(peer)); + } + + //turn off blacklisting + //[TestMethod] + //public void RegisterPeer_BlackListed() + //{ + // var swarm = new SwarmService(_self); + // var peer = new Peer + // { + // Id = "QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h", + // Addresses = new MultiAddress[] + // { + // "/ip4/127.0.0.1/ipfs/QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCM1h" + // } + // }; + + // swarm.BlackList.Add(peer.Addresses.First()); + // ExceptionAssert.Throws(() => swarm.RegisterPeer(peer)); + //} + } + + /// + /// A noop private network. + /// + internal sealed class OpenNetwork : INetworkProtector + { + public static int Count; + + public Task ProtectAsync(PeerConnection connection, + CancellationToken cancel = default) + { + Interlocked.Increment(ref Count); + return Task.FromResult(connection.Stream); + } + } +} diff --git a/src/Lib.P2P.Tests/Transports/TcpTest.cs b/src/Lib.P2P.Tests/Transports/TcpTest.cs new file mode 100644 index 0000000000..f4432a7ce9 --- /dev/null +++ b/src/Lib.P2P.Tests/Transports/TcpTest.cs @@ -0,0 +1,232 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Transports; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests.Transports +{ + [TestClass] + public class TcpTest + { + [TestMethod] + public void Connect_Unknown_Port() + { + var tcp = new Tcp(); + ExceptionAssert.Throws(() => + { + var _ = tcp.ConnectAsync("/ip4/127.0.0.1/tcp/32700").Result; + }); + } + + [TestMethod] + public void Connect_Missing_TCP_Port() + { + var tcp = new Tcp(); + ExceptionAssert.Throws(() => + { + var _ = tcp.ConnectAsync("/ip4/127.0.0.1/udp/32700").Result; + }); + ExceptionAssert.Throws(() => + { + var _ = tcp.ConnectAsync("/ip4/127.0.0.1").Result; + }); + } + + [TestMethod] + public void Connect_Missing_IP_Address() + { + var tcp = new Tcp(); + ExceptionAssert.Throws(() => + { + var _ = tcp.ConnectAsync("/tcp/32700").Result; + }); + } + + [TestMethod] + public void Connect_Unknown_Address() + { + var tcp = new Tcp(); + ExceptionAssert.Throws(() => + { + var _ = tcp.ConnectAsync("/ip4/127.0.10.10/tcp/32700").Result; + }); + } + + [TestMethod] + public void Connect_Cancelled() + { + var tcp = new Tcp(); + var cs = new CancellationTokenSource(); + cs.Cancel(); + ExceptionAssert.Throws(() => + { + var _ = tcp.ConnectAsync("/ip4/127.0.10.10/tcp/32700", cs.Token).Result; + }); + } + + [TestMethod] + [Ignore("Inconsistent results")] + public async Task TimeProtocol() + { + var cs = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var server = await new MultiAddress("/dns4/time.nist.gov/tcp/37").ResolveAsync(cs.Token); + var data = new byte[4]; + + var tcp = new Tcp(); + await using (var time = await tcp.ConnectAsync(server[0], cs.Token)) + { + var n = await time.ReadAsync(data, 0, data.Length, cs.Token); + Assert.AreEqual(4, n); // sometimes zero! + } + } + + [TestMethod] + public void Listen_Then_Cancel() + { + var tcp = new Tcp(); + var cs = new CancellationTokenSource(); + + void Handler(Stream stream, MultiAddress local, MultiAddress remote) + { + Assert.Fail("handler should not be called"); + } + + var listenerAddress = tcp.Listen("/ip4/127.0.0.1", Handler, cs.Token); + Assert.IsTrue(listenerAddress.Protocols.Any(p => p.Name == "tcp")); + cs.Cancel(); + } + + [TestMethod] + public async Task Listen() + { + var tcp = new Tcp(); + var cs = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var connected = false; + MultiAddress listenerAddress = null; + + void Handler(Stream stream, MultiAddress local, MultiAddress remote) + { + Assert.IsNotNull(stream); + Assert.AreEqual(listenerAddress, local); + Assert.IsNotNull(remote); + Assert.AreNotEqual(local, remote); + connected = true; + } + + try + { + listenerAddress = tcp.Listen("/ip4/127.0.0.1", Handler, cs.Token); + Assert.IsTrue(listenerAddress.Protocols.Any(p => p.Name == "tcp")); + await using (var stream = await tcp.ConnectAsync(listenerAddress, cs.Token)) + { + await Task.Delay(50, cs.Token); + Assert.IsNotNull(stream); + Assert.IsTrue(connected); + } + } + finally + { + cs.Cancel(); + } + } + + [TestMethod] + public async Task Listen_Handler_Throws() + { + var tcp = new Tcp(); + var cs = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var called = false; + + void Handler(Stream stream, MultiAddress local, MultiAddress remote) + { + called = true; + throw new Exception("foobar"); + } + + try + { + var addr = tcp.Listen("/ip4/127.0.0.1", Handler, cs.Token); + Assert.IsTrue(addr.Protocols.Any(p => p.Name == "tcp")); + await using (var stream = await tcp.ConnectAsync(addr, cs.Token)) + { + await Task.Delay(50, cs.Token); + Assert.IsNotNull(stream); + Assert.IsTrue(called); + } + } + finally + { + cs.Cancel(); + } + } + + [TestMethod] + public async Task SendReceive() + { + var cs = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var tcp = new Tcp(); + + using (var server = new HelloServer()) + { + await using (var stream = await tcp.ConnectAsync(server.Address, cs.Token)) + { + var bytes = new byte[5]; + await stream.ReadAsync(bytes, 0, bytes.Length, cs.Token); + Assert.AreEqual("hello", Encoding.UTF8.GetString(bytes)); + } + } + } + + private sealed class HelloServer : IDisposable + { + private readonly CancellationTokenSource _cs = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + + public HelloServer() + { + var tcp = new Tcp(); + Address = tcp.Listen("/ip4/127.0.0.1", Handler, _cs.Token); + } + + public MultiAddress Address { get; } + + public void Dispose() { _cs.Cancel(); } + + private static void Handler(Stream stream, MultiAddress local, MultiAddress remote) + { + var msg = Encoding.UTF8.GetBytes("hello"); + stream.Write(msg, 0, msg.Length); + stream.Flush(); + stream.Dispose(); + } + } + } +} diff --git a/src/Lib.P2P.Tests/Transports/UdpTest.cs b/src/Lib.P2P.Tests/Transports/UdpTest.cs new file mode 100644 index 0000000000..f06ad7cd66 --- /dev/null +++ b/src/Lib.P2P.Tests/Transports/UdpTest.cs @@ -0,0 +1,174 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Transports; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats; + +namespace Lib.P2P.Tests.Transports +{ + [TestClass] + public class UdpTest + { + [TestMethod] + public void Connect_Missing_UDP_Port() + { + var udp = new Udp(); + ExceptionAssert.Throws(() => + { + var _ = udp.ConnectAsync("/ip4/127.0.0.1/tcp/32700").Result; + }); + ExceptionAssert.Throws(() => + { + var _ = udp.ConnectAsync("/ip4/127.0.0.1").Result; + }); + } + + [TestMethod] + public void Connect_Missing_IP_Address() + { + var udp = new Udp(); + ExceptionAssert.Throws(() => + { + var _ = udp.ConnectAsync("/udp/32700").Result; + }); + } + + [TestMethod] + public void Connect_Cancelled() + { + var udp = new Udp(); + var cs = new CancellationTokenSource(); + cs.Cancel(); + ExceptionAssert.Throws(() => + { + var _ = udp.ConnectAsync("/ip4/127.0.10.10/udp/32700", cs.Token).Result; + }); + } + + [TestMethod] + [Ignore] + public async Task Listen() + { + var udp = new Udp(); + var cs = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var connected = false; + + void Handler(Stream stream, MultiAddress local, MultiAddress remote) + { + Assert.IsNotNull(stream); + connected = true; + } + + try + { + var listenerAddress = udp.Listen("/ip4/127.0.0.1", Handler, cs.Token); + Assert.IsTrue(listenerAddress.Protocols.Any(p => p.Name == "udp")); + await using (var stream = await udp.ConnectAsync(listenerAddress, cs.Token)) + { + await Task.Delay(50, cs.Token); + Assert.IsNotNull(stream); + Assert.IsTrue(connected); + } + } + finally + { + cs.Cancel(); + } + } + + [TestMethod] + [Ignore("Sometimes fails")] + public async Task NetworkTimeProtocol() + { + var cs = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var server = await new MultiAddress("/dns4/time.windows.com/udp/123").ResolveAsync(cs.Token); + var ntpData = new byte[48]; + ntpData[0] = 0x1B; + + var udp = new Udp(); + await using (var time = await udp.ConnectAsync(server[0], cs.Token)) + { + ntpData[0] = 0x1B; + await time.WriteAsync(ntpData, 0, ntpData.Length, cs.Token); + await time.FlushAsync(cs.Token); + await time.ReadAsync(ntpData, 0, ntpData.Length, cs.Token); + Assert.AreNotEqual(0x1B, ntpData[0]); + + Array.Clear(ntpData, 0, ntpData.Length); + ntpData[0] = 0x1B; + await time.WriteAsync(ntpData, 0, ntpData.Length, cs.Token); + await time.FlushAsync(cs.Token); + await time.ReadAsync(ntpData, 0, ntpData.Length, cs.Token); + Assert.AreNotEqual(0x1B, ntpData[0]); + } + } + + [TestMethod] + [Ignore] + public async Task SendReceive() + { + var cs = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + var udp = new Udp(); + + using (var server = new HelloServer()) + { + await using (var stream = await udp.ConnectAsync(server.Address, cs.Token)) + { + var bytes = new byte[5]; + await stream.ReadAsync(bytes, 0, bytes.Length, cs.Token); + Assert.AreEqual("hello", Encoding.UTF8.GetString(bytes)); + } + } + } + + private sealed class HelloServer : IDisposable + { + private readonly CancellationTokenSource _cs = new CancellationTokenSource(TimeSpan.FromSeconds(30)); + + public HelloServer() + { + var udp = new Udp(); + Address = udp.Listen("/ip4/127.0.0.1", Handler, _cs.Token); + } + + public MultiAddress Address { get; } + + public void Dispose() { _cs.Cancel(); } + + private static void Handler(Stream stream, MultiAddress local, MultiAddress remote) + { + var msg = Encoding.UTF8.GetBytes("hello"); + stream.Write(msg, 0, msg.Length); + stream.Flush(); + stream.Dispose(); + } + } + } +} diff --git a/src/Lib.P2P.Tests/WhiteList.cs b/src/Lib.P2P.Tests/WhiteList.cs new file mode 100644 index 0000000000..eeb1c2b801 --- /dev/null +++ b/src/Lib.P2P.Tests/WhiteList.cs @@ -0,0 +1,50 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace Lib.P2P.Tests +{ + [TestClass] + public class WhiteListTest + { + [TestMethod] + public void Allowed() + { + var policy = new WhiteList(); + policy.Add("a"); + policy.Add("b"); + Assert.IsTrue(policy.IsAllowed("a")); + Assert.IsTrue(policy.IsAllowed("b")); + Assert.IsFalse(policy.IsAllowed("c")); + Assert.IsFalse(policy.IsAllowed("d")); + } + + [TestMethod] + public void Empty() + { + var policy = new WhiteList(); + Assert.IsTrue(policy.IsAllowed("a")); + } + } +} diff --git a/src/Lib.P2P/AutoDialer.cs b/src/Lib.P2P/AutoDialer.cs new file mode 100644 index 0000000000..2d5f4a56ed --- /dev/null +++ b/src/Lib.P2P/AutoDialer.cs @@ -0,0 +1,193 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using System.Threading; +using Common.Logging; + +namespace Lib.P2P +{ + /// + /// Maintains a minimum number of peer connections. + /// + /// + /// Listens to the and automically dials a + /// new when required. + /// + public sealed class AutoDialer : IDisposable + { + private static readonly ILog Log = LogManager.GetLogger(typeof(AutoDialer)); + + /// + /// The default minimum number of connections to maintain (16). + /// + public const int DefaultMinConnections = 16; + + private readonly ISwarmService _swarmService; + private int _pendingConnects; + + /// + /// Creates a new instance of the class. + /// + /// + /// Provides access to other peers. + /// + public AutoDialer(ISwarmService swarmService) + { + _swarmService = swarmService; + swarmService.PeerDiscovered += OnPeerDiscovered; + swarmService.PeerDisconnected += OnPeerDisconnected; + } + + /// + /// Releases the unmanaged and optionally managed resources. + /// + /// + /// true to release both managed and unmanaged resources; false + /// to release only unmanaged resources. + /// + private void Dispose(bool disposing) + { + if (!disposing) + { + return; + } + + _swarmService.PeerDiscovered -= OnPeerDiscovered; + _swarmService.PeerDisconnected -= OnPeerDisconnected; + } + + /// + /// Performs application-defined tasks associated with freeing, + /// releasing, or resetting unmanaged resources. + /// + public void Dispose() { Dispose(true); } + + /// + /// The low water mark for peer connections. + /// + /// + /// Defaults to . + /// + /// + /// Setting this to zero will basically disable the auto dial features. + /// + public int MinConnections { get; set; } = DefaultMinConnections; + +#pragma warning disable VSTHRD100 // Avoid async void methods + /// + /// Called when the swarm has a new peer. + /// + /// + /// The swarm of peers. + /// + /// + /// The peer that was discovered. + /// + /// + /// If the is not reached, then the + /// is dialed. + /// + private async void OnPeerDiscovered(object sender, Peer peer) +#pragma warning restore VSTHRD100 // Avoid async void methods + { + var n = _swarmService.Manager.Connections.Count() + _pendingConnects; + + if (!_swarmService.IsRunning || n >= MinConnections) + { + return; + } + + Interlocked.Increment(ref _pendingConnects); + Log.Debug($"Dialing new {peer}"); + try + { + await _swarmService.ConnectAsync(peer).ConfigureAwait(false); + } + catch (Exception) + { + Log.Warn($"Failed to dial {peer}"); + } + finally + { + Interlocked.Decrement(ref _pendingConnects); + } + } + +#pragma warning disable VSTHRD100 // Avoid async void methods + /// + /// Called when the swarm has lost a connection to a peer. + /// + /// + /// The swarm of peers. + /// + /// + /// The peer that was disconnected. + /// + /// + /// If the is not reached, then another + /// peer is dialed. + /// + private async void OnPeerDisconnected(object sender, Peer disconnectedPeer) +#pragma warning restore VSTHRD100 // Avoid async void methods + { + var n = _swarmService.Manager.Connections.Count() + _pendingConnects; + if (!_swarmService.IsRunning || n >= MinConnections) + { + return; + } + + // Find a random peer to connect with. + var peers = _swarmService.KnownPeers + .Where(p => p.ConnectedAddress == null) + .Where(p => p != disconnectedPeer) + .Where(p => _swarmService.IsAllowed(p)) + .Where(p => !_swarmService.HasPendingConnection(p)) + .ToArray(); + + if (peers.Length == 0) + { + return; + } + + var rng = new Random(); + var peer = peers[rng.Next(peers.Length)]; + + Interlocked.Increment(ref _pendingConnects); + Log.Debug($"Dialing {peer}"); + try + { + await _swarmService.ConnectAsync(peer).ConfigureAwait(false); + } + catch (Exception) + { + Log.Warn($"Failed to dial {peer}"); + } + finally + { + Interlocked.Decrement(ref _pendingConnects); + } + } + } +} diff --git a/src/Lib.P2P/BlackList.cs b/src/Lib.P2P/BlackList.cs new file mode 100644 index 0000000000..aeb4cc1acc --- /dev/null +++ b/src/Lib.P2P/BlackList.cs @@ -0,0 +1,45 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Linq; + +namespace Lib.P2P +{ + /// + /// A sequence of targets that are not approved. + /// + /// + /// The type of object that the rule applies to. + /// + /// + /// Only targets that are not defined will pass. + /// + public class BlackList : ConcurrentBag, IPolicy + where T : IEquatable + { + /// + public bool IsAllowed(T target) { return !this.Contains(target); } + } +} diff --git a/src/Lib.P2P/Cid.cs b/src/Lib.P2P/Cid.cs new file mode 100644 index 0000000000..611a4dc34d --- /dev/null +++ b/src/Lib.P2P/Cid.cs @@ -0,0 +1,615 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Text; +using Google.Protobuf; +using MultiFormats; +using Newtonsoft.Json; + +namespace Lib.P2P +{ + /// + /// Identifies some content, e.g. a Content ID. + /// + /// + /// + /// A Cid is a self-describing content-addressed identifier for distributed systems. + /// + /// + /// Initially, IPFS used a as the CID and this is still supported as 0. + /// Version 1 adds a self describing structure to the multi-hash, see the spec. + /// + /// + /// The hashing algorithm must be "sha2-256" for a version 0 CID. + /// + /// + /// + [JsonConverter(typeof(CidJsonConverter))] + public sealed partial class Cid : IEquatable + { + /// + /// The default . + /// + public const string DefaultContentType = "dag-pb"; + + private string _encodedValue; + private int _version; + private string _encoding = MultiBase.DefaultAlgorithmName; + private string _contentType = DefaultContentType; + private MultiHash _hash; + + /// + /// Throws if a property cannot be set. + /// + /// + /// When a property cannot be set. + /// + /// + /// Once is invoked, the CID's properties + /// cannot be set. + /// + private void EnsureMutable() + { + if (_encodedValue != null) + { + throw new NotSupportedException("CID cannot be changed."); + } + } + + /// + /// The version of the CID. + /// + /// + /// 0 or 1. + /// + /// + /// + /// When the is 0 and the following properties + /// are not matched, then the version is upgraded to version 1 when any + /// of the properties is set. + /// + /// equals "dag-pb" + /// equals "base58btc" + /// algorithm name equals "sha2-256" + /// + /// + /// + /// + /// The default is "base32" when the + /// is not zero. + /// + public int Version + { + get => _version; + set + { + EnsureMutable(); + + if (_version == 0 && value > 0 && Encoding == "base58btc") + { + _encoding = "base32"; + } + + _version = value; + } + } + + /// + /// The encoding of the CID. + /// + /// + /// base58btc, base32, base64, etc. Defaults to . + /// + /// + /// Sets to 1, when setting a value that + /// is not equal to "base58btc". + /// + /// + public string Encoding + { + get => _encoding; + set + { + EnsureMutable(); + if (Version == 0 && value != "base58btc") + { + Version = 1; + } + + _encoding = value; + } + } + + /// + /// The content type or format of the data being addressed. + /// + /// + /// dag-pb, dag-cbor, eth-block, etc. Defaults to "dag-pb". + /// + /// + /// Sets to 1, when setting a value that + /// is not equal to "dag-pb". + /// + /// + public string ContentType + { + get => _contentType; + set + { + EnsureMutable(); + _contentType = value; + if (Version == 0 && value != "dag-pb") + { + Version = 1; + } + } + } + + /// + /// The cryptographic hash of the content being addressed. + /// + /// + /// The of the content being addressed. + /// + /// + /// Sets to 1, when setting a hashing algorithm that + /// is not equal to "sha2-256". + /// + /// If the equals identity, then + /// the is also the content. This is commonly + /// referred to as 'CID inlining'. + /// + /// + public MultiHash Hash + { + get => _hash; + set + { + EnsureMutable(); + _hash = value; + if (Version == 0 && Hash.Algorithm.Name != "sha2-256") + { + Version = 1; + } + } + } + + /// + /// Returns the string representation of the CID in the + /// general format. + /// + /// + /// e.g. "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V" + /// + public override string ToString() { return ToString("G"); } + + /// + /// Returns a string representation of the CID + /// according to the provided format specifier. + /// + /// + /// A single format specifier that indicates how to format the value of the + /// CID. Can be "G" or "L". + /// + /// + /// The CID in the specified . + /// + /// + /// is not valid. + /// + /// + /// + /// The "G" format specifier is the same as calling . + /// + /// + /// + /// Specifier + /// return value + /// + /// + /// G + /// QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V + /// + /// + /// L + /// base58btc cidv0 dag-pb sha2-256 Qm... + /// + /// + /// + public string ToString(string format) + { + switch (format) + { + case "G": + return Encode(); + + case "L": + StringBuilder sb = new(); + sb.Append(Encoding); + sb.Append(' '); + sb.Append("cidv"); + sb.Append(Version); + sb.Append(' '); + sb.Append(ContentType); + + if (Hash == null) + { + return sb.ToString(); + } + + sb.Append(' '); + sb.Append(Hash.Algorithm.Name); + sb.Append(' '); + sb.Append(MultiBase.Encode(Hash.ToArray(), Encoding).Substring(1)); + + return sb.ToString(); + + default: + throw new FormatException($"Invalid CID format specifier '{format}'."); + } + } + + /// + /// Converts the CID to its equivalent string representation. + /// + /// + /// The string representation of the . + /// + /// + /// For 0, this is equalivalent to the + /// base58btc encoding + /// of the . + /// + /// + public string Encode() + { + if (_encodedValue != null) + { + return _encodedValue; + } + + switch (Version) + { + case 0: + _encodedValue = Hash.ToBase58(); + break; + default: + { + using (MemoryStream ms = new()) + { + ms.WriteVarint(Version); + ms.WriteMultiCodec(ContentType); + Hash.Write(ms); + _encodedValue = MultiBase.Encode(ms.ToArray(), Encoding); + } + + break; + } + } + + return _encodedValue; + } + + /// + /// Converts the specified + /// to an equivalent object. + /// + /// + /// The CID encoded string. + /// + /// + /// A new that is equivalent to . + /// + /// + /// When the can not be decoded. + /// + /// + public static Cid Decode(string input) + { + try + { + // SHA2-256 MultiHash is CID v0. + if (input.Length == 46 && input.StartsWith("Qm")) + { + return new MultiHash(input); + } + + using (MemoryStream ms = new(MultiBase.Decode(input), false)) + { + var v = ms.ReadVarint32(); + if (v != 1) + { + throw new InvalidDataException($"Unknown CID version '{v}'."); + } + + return new Cid + { + Version = v, + Encoding = MultiFormats.Registry.MultiBaseAlgorithm.Codes[input[0]].Name, + ContentType = ms.ReadMultiCodec().Name, + Hash = new MultiHash(ms) + }; + } + } + catch (Exception e) + { + throw new FormatException($"Invalid CID '{input}'.", e); + } + } + + /// + /// Reads the binary representation of the CID from the specified . + /// + /// + /// The to read from. + /// + /// + /// A new . + /// + public static Cid Read(Stream stream) + { + Cid cid = new(); + var length = stream.ReadVarint32(); + if (length == 34) + { + cid.Version = 0; + } + else + { + cid.Version = stream.ReadVarint32(); + cid.ContentType = stream.ReadMultiCodec().Name; + } + + cid.Hash = new MultiHash(stream); + + return cid; + } + + /// + /// Writes the binary representation of the CID to the specified . + /// + /// + /// The to write to. + /// + public void Write(Stream stream) + { + using (MemoryStream ms = new()) + { + if (Version != 0) + { + ms.WriteVarint(Version); + ms.WriteMultiCodec(ContentType); + } + + Hash.Write(ms); + + stream.WriteVarint(ms.Length); + ms.Position = 0; + ms.CopyTo(stream); + } + } + + /// + /// Reads the binary representation of the CID from the specified . + /// + /// + /// The to read from. + /// + /// + /// A new . + /// + public static Cid Read(CodedInputStream stream) + { + Cid cid = new(); + var length = stream.ReadLength(); + if (length == 34) + { + cid.Version = 0; + } + else + { + cid.Version = stream.ReadInt32(); + cid.ContentType = stream.ReadMultiCodec().Name; + } + + cid.Hash = new MultiHash(stream); + + return cid; + } + + /// + /// Writes the binary representation of the CID to the specified . + /// + /// + /// The to write to. + /// + public void Write(CodedOutputStream stream) + { + using (MemoryStream ms = new()) + { + if (Version != 0) + { + ms.WriteVarint(Version); + ms.WriteMultiCodec(ContentType); + } + + Hash.Write(ms); + + var bytes = ms.ToArray(); + stream.WriteLength(bytes.Length); + stream.WriteSomeBytes(bytes); + } + } + + /// + /// Reads the binary representation of the CID from the specified byte array. + /// + /// + /// The souce of a CID. + /// + /// + /// A new . + /// + /// + /// The buffer does NOT start with a varint length prefix. + /// + public static Cid Read(byte[] buffer) + { + Cid cid = new(); + if (buffer.Length == 34) + { + cid.Version = 0; + cid.Hash = new MultiHash(buffer); + return cid; + } + + using (MemoryStream ms = new(buffer, false)) + { + cid.Version = ms.ReadVarint32(); + cid.ContentType = ms.ReadMultiCodec().Name; + cid.Hash = new MultiHash(ms); + return cid; + } + } + + /// + /// Returns the binary representation of the CID as a byte array. + /// + /// + /// A new buffer containing the CID. + /// + /// + /// The buffer does NOT start with a varint length prefix. + /// + public byte[] ToArray() + { + if (Version == 0) + { + return Hash.ToArray(); + } + + using (MemoryStream ms = new()) + { + ms.WriteVarint(Version); + ms.WriteMultiCodec(ContentType); + Hash.Write(ms); + return ms.ToArray(); + } + } + + /// + /// Implicit casting of a to a . + /// + /// + /// A . + /// + /// + /// A new based on the . A 0 + /// CID is returned if the is "sha2-356"; otherwise 1. + /// + public static implicit operator Cid(MultiHash hash) + { + if (hash.Algorithm.Name == "sha2-256") + { + return new Cid + { + Hash = hash, + Version = 0, + Encoding = "base58btc", + ContentType = "dag-pb" + }; + } + + return new Cid + { + Version = 1, + Hash = hash + }; + } + + /// + public override int GetHashCode() + { + return Encode().GetHashCode(); + } + + /// + public override bool Equals(object obj) + { + var that = obj as Cid; + return that != null && Encode() == that.Encode(); + } + + /// + public bool Equals(Cid that) { return Encode() == that?.Encode(); } + + /// + /// Value equality. + /// + public static bool operator ==(Cid a, Cid b) + { + return ReferenceEquals(a, b) || !ReferenceEquals(a, null) && !ReferenceEquals(b, null) && a.Equals(b); + } + + /// + /// Value inequality. + /// + public static bool operator !=(Cid a, Cid b) + { + return !ReferenceEquals(a, b) && (ReferenceEquals(a, null) || ReferenceEquals(b, null) || !a.Equals(b)); + } + + /// + /// Implicit casting of a to a . + /// + /// + /// A string encoded Cid. + /// + /// + /// A new . + /// + /// + /// Equivalent to Cid.Decode(s) + /// + public static implicit operator Cid(string s) { return Decode(s); } + + /// + /// Implicit casting of a to a . + /// + /// + /// A Cid. + /// + /// + /// A new . + /// + /// + /// Equivalent to Cid.Encode() + /// + public static implicit operator string(Cid id) + { + return id.Encode(); + } + } +} diff --git a/src/Lib.P2P/CidJsonConverter.cs b/src/Lib.P2P/CidJsonConverter.cs new file mode 100644 index 0000000000..658631a173 --- /dev/null +++ b/src/Lib.P2P/CidJsonConverter.cs @@ -0,0 +1,65 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Newtonsoft.Json; + +namespace Lib.P2P +{ + public sealed partial class Cid + { + /// + /// Conversion of a to and from JSON. + /// + /// + /// The JSON is just a single string value. + /// + public class CidJsonConverter : JsonConverter + { + /// + public override bool CanConvert(Type objectType) { return true; } + + /// + public override bool CanRead => true; + + /// + public override bool CanWrite => true; + + /// + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var cid = value as Cid; + writer.WriteValue(cid?.Encode()); + } + + /// + public override object ReadJson(JsonReader reader, + Type objectType, + object existingValue, + JsonSerializer serializer) + { + return !(reader.Value is string s) ? null : Decode(s); + } + } + } +} diff --git a/src/Lib.P2P/ConnectionManager.cs b/src/Lib.P2P/ConnectionManager.cs new file mode 100644 index 0000000000..fb105561f3 --- /dev/null +++ b/src/Lib.P2P/ConnectionManager.cs @@ -0,0 +1,252 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using MultiFormats; + +namespace Lib.P2P +{ + /// + /// Manages the peer connections in a . + /// + /// + /// Enforces that only one connection exists to the . This + /// prevents the race condition when two simultaneously connect to each other. + /// + /// TODO: Enforces a maximum number of open connections. + /// + /// + public sealed class ConnectionManager + { + /// + /// The connections to other peers. Key is the base58 hash of the peer ID. + /// + private readonly ConcurrentDictionary> _connections = + new(); + + private static string Key(Peer peer) { return peer.Id.ToBase58(); } + + private static string Key(MultiHash id) { return id.ToBase58(); } + + /// + /// Raised when a peer's connection is closed. + /// + public event EventHandler PeerDisconnected; + + /// + /// Gets the current active connections. + /// + public IEnumerable Connections => + _connections.Values + .SelectMany(c => c) + .Where(c => c.IsActive); + + /// + /// Determines if a connection exists to the specified peer. + /// + /// + /// Another peer. + /// + /// + /// true if there is a connection to the and + /// the connection is active; otherwise false. + /// + public bool IsConnected(Peer peer) { return TryGet(peer, out _); } + + /// + /// Gets the connection to the peer. + /// + /// + /// A peer. + /// + /// + /// The connection to the peer. + /// + /// + /// true if a connection exists; otherwise false. + /// + /// + /// If the connection's underlaying + /// is closed, then the connection is removed. + /// + internal bool TryGet(Peer peer, out PeerConnection connection) + { + connection = null; + if (!_connections.TryGetValue(Key(peer), out var conns)) + { + return false; + } + + connection = conns + .FirstOrDefault(c => c.IsActive); + + return connection != null; + } + + /// + /// Adds a new connection. + /// + /// + /// The to add. + /// + /// + /// The connection that should be used. + /// + /// + /// If a connection already exists to the peer, the specified + /// is closed and existing connection + /// is returned. + /// + public PeerConnection Add(PeerConnection connection) + { + if (connection == null) + { + throw new ArgumentNullException(nameof(connection)); + } + + if (connection.RemotePeer == null) + { + throw new ArgumentNullException(nameof(connection.RemotePeer)); + } + + if (connection.RemotePeer.Id == null) + { + throw new ArgumentNullException(nameof(connection.RemotePeer.Id)); + } + + _connections.AddOrUpdate( + Key(connection.RemotePeer), + key => new List {connection}, + (key, conns) => + { + if (!conns.Contains(connection)) + { + conns.Add(connection); + } + + return conns; + } + ); + + if (connection.RemotePeer.ConnectedAddress == null) + { + connection.RemotePeer.ConnectedAddress = connection.RemoteAddress; + } + + connection.Closed += (s, e) => Remove(e); + return connection; + } + + /// + /// Remove a connection. + /// + /// + /// The to remove. + /// + /// + /// true if the connection was removed; otherwise, false. + /// + /// + /// The is removed from the list of + /// connections and is closed. + /// + public bool Remove(PeerConnection connection) + { + if (connection == null) + { + return false; + } + + if (!_connections.TryGetValue(Key(connection.RemotePeer), out var originalConns)) + { + connection.Dispose(); + return false; + } + + if (!originalConns.Contains(connection)) + { + connection.Dispose(); + return false; + } + + List newConns = new(); + newConns.AddRange(originalConns.Where(c => c != connection)); + _connections.TryUpdate(Key(connection.RemotePeer), newConns, originalConns); + + connection.Dispose(); + if (newConns.Count > 0) + { + var last = newConns.Last(); + last.RemotePeer.ConnectedAddress = last.RemoteAddress; + } + else + { + connection.RemotePeer.ConnectedAddress = null; + PeerDisconnected?.Invoke(this, connection.RemotePeer.Id); + } + + return true; + } + + /// + /// Remove and close all connection to the peer ID. + /// + /// + /// The ID of a to remove. + /// + /// + /// true if a connection was removed; otherwise, false. + /// + public bool Remove(MultiHash id) + { + if (!_connections.TryRemove(Key(id), out var conns)) + { + return false; + } + + foreach (var conn in conns) + { + conn.RemotePeer.ConnectedAddress = null; + conn.Dispose(); + } + + PeerDisconnected?.Invoke(this, id); + return true; + } + + /// + /// Removes and closes all connections. + /// + public void Clear() + { + var conns = _connections.Values.SelectMany(c => c).ToArray(); + foreach (var conn in conns) + { + Remove(conn); + } + } + } +} diff --git a/src/Lib.P2P/Cryptography/CtrStreamCipher.cs b/src/Lib.P2P/Cryptography/CtrStreamCipher.cs new file mode 100644 index 0000000000..8bc41d3ed1 --- /dev/null +++ b/src/Lib.P2P/Cryptography/CtrStreamCipher.cs @@ -0,0 +1,167 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Utilities; + +namespace Lib.P2P.Cryptography +{ + /// + /// The CTR cipher as a stream. + /// + /// + /// A copy of SicBlockCipher + /// that implements . + /// + public sealed class CtrStreamCipher : IStreamCipher + { + private readonly IBlockCipher _cipher; + private readonly int _blockSize; + private readonly byte[] _counter; + private readonly byte[] _counterOut; + private int _byteCount; + private byte[] _iv; + + /// + /// Creates a new instance of the with + /// the specified cipher. + /// + /// + /// The cipher to produce the output counter. Typically + /// . + /// + public CtrStreamCipher(IBlockCipher cipher) + { + _cipher = cipher; + _blockSize = cipher.GetBlockSize(); + _counter = new byte[_blockSize]; + _counterOut = new byte[_blockSize]; + _iv = new byte[_blockSize]; + } + + /// + /// The name of this algorithm. + /// + public string AlgorithmName => _cipher.AlgorithmName + "/CTR"; + + /// + /// Init the cipher. + /// + /// + /// Ignored. + /// + /// + /// Must be a . + /// + /// + /// var encrypt = new CtrStreamCipher(new AesEngine()); + /// var p = new ParametersWithIV(new KeyParameter(key), iv); + /// encrypt.Init(true, p); + /// + public void Init(bool forEncryption, ICipherParameters parameters) + { + var ivParam = parameters as ParametersWithIV; + if (ivParam == null) + { + throw new ArgumentException("CTR mode requires ParametersWithIV", nameof(parameters)); + } + + _iv = Arrays.Clone(ivParam.GetIV()); + + if (_blockSize < _iv.Length) + { + throw new ArgumentException("CTR mode requires IV no greater than: " + _blockSize + " bytes."); + } + + var maxCounterSize = Math.Min(8, _blockSize / 2); + if (_blockSize - _iv.Length > maxCounterSize) + { + throw new ArgumentException("CTR mode requires IV of at least: " + (_blockSize - maxCounterSize) + + " bytes."); + } + + // if null it's an IV changed only. + if (ivParam.Parameters != null) + { + _cipher.Init(true, ivParam.Parameters); + } + + Reset(); + } + + /// + public void Reset() + { + _byteCount = 0; + Arrays.Fill(_counter, 0); + Array.Copy(_iv, 0, _counter, 0, _iv.Length); + _cipher.Reset(); + } + + /// + public void ProcessBytes(byte[] input, int inOff, int length, byte[] output, int outOff) + { + if (outOff + length > output.Length) + { + throw new DataLengthException("Output buffer too short"); + } + + if (inOff + length > input.Length) + { + throw new DataLengthException("Input buffer too small"); + } + + var inStart = inOff; + var inEnd = inOff + length; + var outStart = outOff; + + while (inStart < inEnd) + { + output[outStart++] = ReturnByte(input[inStart++]); + } + } + + /// + public byte ReturnByte(byte input) + { + if (_byteCount == 0) + { + _cipher.ProcessBlock(_counter, 0, _counterOut, 0); + + // Increment the counter + var j = _counter.Length; + while (--j >= 0 && ++_counter[j] == 0) { } + } + + var rv = (byte) (_counterOut[_byteCount++] ^ input); + if (_byteCount == _counterOut.Length) + { + _byteCount = 0; + } + + return rv; + } + } +} diff --git a/src/Lib.P2P/Cryptography/EphermalKey.cs b/src/Lib.P2P/Cryptography/EphermalKey.cs new file mode 100644 index 0000000000..1e2877ac65 --- /dev/null +++ b/src/Lib.P2P/Cryptography/EphermalKey.cs @@ -0,0 +1,131 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using Org.BouncyCastle.Asn1.X9; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using Org.BouncyCastle.Utilities; + +namespace Lib.P2P.Cryptography +{ + /// + /// A short term key on a curve. + /// + /// + /// Ephemeral keys are different from other keys in IPFS; they are NOT + /// protobuf encoded and are NOT self describing. The encoding is an + /// uncompressed ECPoint; the first byte s a 4 and followed by X and Y co-ordinates. + /// + /// It as assummed that the curve name is known a priori. + /// + /// + public sealed class EphermalKey + { + private ECPublicKeyParameters _publicKey; + private ECPrivateKeyParameters _privateKey; + + /// + /// Gets the IPFS encoding of the public key. + /// + /// + /// Returns the uncompressed EC point. + /// + internal byte[] PublicKeyBytes() { return _publicKey.Q.GetEncoded(false); } + + /// + /// Create a shared secret between this key and another. + /// + /// + /// Another ephermal key. + /// + /// + /// The shared secret as a byte array. + /// + /// + /// Uses the ECDH agreement algorithm to generate the shared secet. + /// + public byte[] GenerateSharedSecret(EphermalKey other) + { + var agreement = AgreementUtilities.GetBasicAgreement("ECDH"); + agreement.Init(_privateKey); + var secret = agreement.CalculateAgreement(other._publicKey); + return BigIntegers.AsUnsignedByteArray(agreement.GetFieldSize(), secret); + } + + /// + /// Create a public key from the IPFS ephermal encoding. + /// + /// + /// The name of the curve, for example "P-256". + /// + /// + /// The IPFS encoded ephermal key. + /// + internal static EphermalKey CreatePublicKeyFromDfs(string curveName, byte[] bytes) + { + var ecP = ECNamedCurveTable.GetByName(curveName); + if (ecP == null) + { + throw new KeyNotFoundException($"Unknown curve name '{curveName}'."); + } + + ECDomainParameters domain = new(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()); + var q = ecP.Curve.DecodePoint(bytes); + return new EphermalKey + { + _publicKey = new ECPublicKeyParameters(q, domain) + }; + } + + /// + /// Create a new ephermal key on the curve. + /// + /// + /// The name of the curve, for example "P-256". + /// + /// + /// The new created emphermal key. + /// + public static EphermalKey Generate(string curveName) + { + var ecP = ECNamedCurveTable.GetByName(curveName); + if (ecP == null) + { + throw new Exception($"Unknown curve name '{curveName}'."); + } + + ECDomainParameters domain = new(ecP.Curve, ecP.G, ecP.N, ecP.H, ecP.GetSeed()); + var g = GeneratorUtilities.GetKeyPairGenerator("EC"); + g.Init(new ECKeyGenerationParameters(domain, new SecureRandom())); + var keyPair = g.GenerateKeyPair(); + + return new EphermalKey + { + _privateKey = (ECPrivateKeyParameters) keyPair.Private, + _publicKey = (ECPublicKeyParameters) keyPair.Public + }; + } + } +} diff --git a/src/Lib.P2P/Cryptography/Key.cs b/src/Lib.P2P/Cryptography/Key.cs new file mode 100644 index 0000000000..34c47dd552 --- /dev/null +++ b/src/Lib.P2P/Cryptography/Key.cs @@ -0,0 +1,187 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; +using ProtoBuf; + +namespace Lib.P2P.Cryptography +{ + /// + /// An asymmetric key. + /// + public class Key + { + private const string RsaSigningAlgorithmName = "SHA-256withRSA"; + private const string EcSigningAlgorithmName = "SHA-256withECDSA"; + private const string Ed25519SigningAlgorithmName = "Ed25519"; + + private AsymmetricKeyParameter _publicKey; + private AsymmetricKeyParameter _privateKey; + private string _signingAlgorithmName; + + private Key() { } + + /// + /// Verify that signature matches the data. + /// + /// + /// The data to check. + /// + /// + /// The supplied signature of the . + /// + /// + /// The does match the . + /// + public void Verify(byte[] data, byte[] signature) + { + var signer = SignerUtilities.GetSigner(_signingAlgorithmName); + signer.Init(false, _publicKey); + signer.BlockUpdate(data, 0, data.Length); + if (!signer.VerifySignature(signature)) + throw new InvalidDataException("Data does not match the signature."); + } + + /// + /// Create a signature for the data. + /// + /// + /// The data to sign. + /// + /// + /// The signature. + /// + public byte[] Sign(byte[] data) + { + var signer = SignerUtilities.GetSigner(_signingAlgorithmName); + signer.Init(true, _privateKey); + signer.BlockUpdate(data, 0, data.Length); + return signer.GenerateSignature(); + } + + /// + /// Create a public key from the IPFS message. + /// + /// + /// The IPFS encoded protobuf PublicKey message. + /// + /// + /// The public key. + /// + public static Key CreatePublicKeyFromIpfs(byte[] bytes) + { + Key key = new(); + + MemoryStream ms = new(bytes, false); + var ipfsKey = Serializer.Deserialize(ms); + + switch (ipfsKey.Type) + { + case KeyType.Rsa: + key._publicKey = PublicKeyFactory.CreateKey(ipfsKey.Data); + key._signingAlgorithmName = RsaSigningAlgorithmName; + break; + case KeyType.Ed25519: + key._publicKey = PublicKeyFactory.CreateKey(ipfsKey.Data); + key._signingAlgorithmName = Ed25519SigningAlgorithmName; + break; + case KeyType.Secp256K1: + key._publicKey = PublicKeyFactory.CreateKey(ipfsKey.Data); + key._signingAlgorithmName = EcSigningAlgorithmName; + break; + default: + throw new InvalidDataException($"Unknown key type of {ipfsKey.Type}."); + } + + return key; + } + + /// + /// Create the key from the Bouncy Castle private key. + /// + /// + /// The Bouncy Castle private key. + /// + public static Key CreatePrivateKey(AsymmetricKeyParameter privateKey) + { + Key key = new(); + key._privateKey = privateKey; + + // Get the public key from the private key. + if (privateKey is RsaPrivateCrtKeyParameters rsa) + { + key._publicKey = new RsaKeyParameters(false, rsa.Modulus, rsa.PublicExponent); + key._signingAlgorithmName = RsaSigningAlgorithmName; + } + else if (privateKey is Ed25519PrivateKeyParameters ed) + { + key._publicKey = ed.GeneratePublicKey(); + key._signingAlgorithmName = Ed25519SigningAlgorithmName; + } + else if (privateKey is ECPrivateKeyParameters ec) + { + var q = ec.Parameters.G.Multiply(ec.D); + key._publicKey = new ECPublicKeyParameters(q, ec.Parameters); + key._signingAlgorithmName = EcSigningAlgorithmName; + } + + if (key._publicKey == null) + throw new NotSupportedException($"The key type {privateKey.GetType().Name} is not supported."); + + return key; + } + + private enum KeyType + { + Rsa = 0, + Ed25519 = 1, + Secp256K1 = 2, + Ecdh = 4, + } + + [ProtoContract] + private class PublicKeyMessage + { + [ProtoMember(1, IsRequired = true)] + public KeyType Type { get; set; } + + [ProtoMember(2, IsRequired = true)] + public byte[] Data { get; set; } + } + +#if false + [ProtoContract] + class PrivateKeyMessage + { + [ProtoMember(1, IsRequired = true)] + public KeyType Type; + [ProtoMember(2, IsRequired = true)] + public byte[] Data; + } +#endif + } +} diff --git a/src/Lib.P2P/Cryptography/PreSharedKey.cs b/src/Lib.P2P/Cryptography/PreSharedKey.cs new file mode 100644 index 0000000000..388812df1a --- /dev/null +++ b/src/Lib.P2P/Cryptography/PreSharedKey.cs @@ -0,0 +1,167 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Security.Cryptography; +using System.Text; +using MultiFormats; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Lib.P2P.Cryptography +{ + /// + /// A secret (symmetric) key shared among some entities. + /// + /// + /// This is specifically used for nodes in a private network. + /// + /// + public class PreSharedKey + { + private const string CodecName = "/key/swarm/psk/1.0.0/"; + + /// + /// The key value. + /// + /// + /// The value as a byte array. + /// + public byte[] Value { get; set; } + + /// + /// The length of the key's value. + /// + /// + /// The length in bits. + /// + public int Length => Value?.Length * 8 ?? 0; + + /// + /// Gets an ID for the key. + /// + /// + /// A byte array that can be used as an identifier for the key. + /// + /// + /// C# implementation of the GO code at + /// . + /// + public byte[] Fingerprint() + { + // Encrypt data first so we don't feed PSK to hash function. + // Salsa20 function is not reversible thus increasing our security margin. + var encrypted = new byte[64]; + var nonce = Encoding.ASCII.GetBytes("finprint"); + Salsa20Engine cipher = new(); + cipher.Init(true, new ParametersWithIV(new KeyParameter(Value), nonce)); + cipher.ProcessBytes(encrypted, 0, encrypted.Length, encrypted, 0); + + // Then do Shake-128 hash to reduce its length. + // This way if for some reason Shake is broken and Salsa20 preimage is possible, + // attacker has only half of the bytes necessary to recreate psk. + return MultiHash + .GetHashAlgorithm("shake-128") + .ComputeHash(encrypted); + } + + /// + /// Generate a new value of the specified length. + /// + /// + /// The length in bits of the new key value, defaults to 256. + /// + /// + /// this for a fluent design. + /// + public PreSharedKey Generate(int length = 256) + { + using (var rng = RandomNumberGenerator.Create()) + { + Value = new byte[length / 8]; + rng.GetBytes(Value); + } + + return this; + } + + /// + /// Write the key to text stream. + /// + /// + /// A text writer. + /// + /// + /// Determines how the key is formatted. Can be + /// "base16" or "base64". Defaults to "base16". + /// + /// + /// The key is writen as three lines + /// (1) the codec name "/key/swarm/psk/1.0.0/" + /// (2) the base encoding "/base16/" or "/base64/", + /// (3) the key value in the base encoding + /// + public void Export(TextWriter text, string format = "base16") + { + text.WriteLine(CodecName); + text.Write("/"); + text.Write(format); + text.WriteLine("/"); + switch (format) + { + case "base16": + text.WriteLine(Value.ToHexString()); + break; + case "base64": + text.WriteLine(Base64NoPad.ToBase64NoPad(Value)); + break; + default: + throw new Exception($"Unknown encoding '{format}'."); + } + } + + /// + /// Read the key from the text stream. + /// + /// + /// A text reader. + /// + public void Import(TextReader text) + { + if (text.ReadLine() != CodecName) + throw new FormatException($"Expected '{CodecName}'."); + switch (text.ReadLine()) + { + case "/base16/": + Value = text.ReadLine().ToHexBuffer(); + break; + case "/base64/": + Value = Base64NoPad.FromBase64NoPad(text.ReadLine()); + break; + default: + throw new FormatException("Unknown base encoding."); + } + } + } +} diff --git a/src/Lib.P2P/Cryptography/StretchedKey.cs b/src/Lib.P2P/Cryptography/StretchedKey.cs new file mode 100644 index 0000000000..35634222d7 --- /dev/null +++ b/src/Lib.P2P/Cryptography/StretchedKey.cs @@ -0,0 +1,142 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Text; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Lib.P2P.Cryptography +{ + /// + /// Symmetric keys for SECIO. + /// + /// + /// Keys derived from a shared secret. + /// + public class StretchedKey + { + /// + /// The nonce. + /// + public byte[] Iv { get; set; } + + /// + /// The message authentication code. + /// + public byte[] MacKey { get; set; } + + /// + /// The encyption key. + /// + public byte[] CipherKey { get; set; } + + /// + /// Create two streched keys from the shared secret. + /// + /// + /// The is no spec for this. Copied https://github.com/libp2p/go-libp2p-crypto/blob/0f79fbebcb64f746a636aba79ece0635ec5919e9/key.go#L183 + /// + public static void Generate(string cipherName, + string hashName, + byte[] secret, + out StretchedKey k1, + out StretchedKey k2) + { + int cipherKeySize; + int ivSize; + switch (cipherName) + { + case "AES-128": + ivSize = 16; + cipherKeySize = 16; + break; + case "AES-256": + ivSize = 16; + cipherKeySize = 32; + break; + case "Blowfish": + ivSize = 8; + cipherKeySize = 32; + break; + default: + throw new NotSupportedException($"Cipher '{cipherName}' is not supported."); + } + + var hmacKeySize = 20; + var need = 2 * (ivSize + cipherKeySize + hmacKeySize); + + HMac hmac = new(DigestUtilities.GetDigest(hashName)); + KeyParameter kp = new(secret); + var seed = Encoding.ASCII.GetBytes("key expansion"); + hmac.Init(kp); + var a = new byte[hmac.GetMacSize()]; + var b = new byte[hmac.GetMacSize()]; + + hmac.BlockUpdate(seed, 0, seed.Length); + hmac.DoFinal(a, 0); + + var j = 0; + var result = new byte[need]; + while (j < need) + { + hmac.Reset(); + hmac.BlockUpdate(a, 0, a.Length); + hmac.BlockUpdate(seed, 0, seed.Length); + hmac.DoFinal(b, 0); + + var todo = b.Length; + if (j + todo > need) + todo = need - j; + Buffer.BlockCopy(b, 0, result, j, todo); + j += todo; + + hmac.Reset(); + hmac.BlockUpdate(a, 0, a.Length); + hmac.DoFinal(a, 0); + } + + var half = need / 2; + k1 = new StretchedKey + { + Iv = new byte[ivSize], + CipherKey = new byte[cipherKeySize], + MacKey = new byte[hmacKeySize] + }; + Buffer.BlockCopy(result, 0, k1.Iv, 0, ivSize); + Buffer.BlockCopy(result, ivSize, k1.CipherKey, 0, cipherKeySize); + Buffer.BlockCopy(result, ivSize + cipherKeySize, k1.MacKey, 0, hmacKeySize); + + k2 = new StretchedKey + { + Iv = new byte[ivSize], + CipherKey = new byte[cipherKeySize], + MacKey = new byte[hmacKeySize] + }; + Buffer.BlockCopy(result, half, k2.Iv, 0, ivSize); + Buffer.BlockCopy(result, half + ivSize, k2.CipherKey, 0, cipherKeySize); + Buffer.BlockCopy(result, half + ivSize + cipherKeySize, k2.MacKey, 0, hmacKeySize); + } + } +} diff --git a/src/Lib.P2P/Discovery/Bootstrap.cs b/src/Lib.P2P/Discovery/Bootstrap.cs new file mode 100644 index 0000000000..b021ef454d --- /dev/null +++ b/src/Lib.P2P/Discovery/Bootstrap.cs @@ -0,0 +1,89 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Common.Logging; +using MultiFormats; + +namespace Lib.P2P.Discovery +{ + /// + /// Discovers the pre-configured peers. + /// + public class Bootstrap : IPeerDiscovery + { + private static ILog _log = LogManager.GetLogger(typeof(Bootstrap)); + + /// + public event EventHandler PeerDiscovered; + + /// + /// The addresses of the pre-configured peers. + /// + /// + /// Each address must end with the ipfs protocol and the public ID + /// of the peer. For example "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" + /// + public IEnumerable Addresses { get; set; } + + /// + public Task StartAsync() + { + _log.Debug("Starting"); + if (Addresses == null) + { + _log.Warn("No bootstrap addresses"); + return Task.CompletedTask; + } + + var peers = Addresses + .Where(a => a.HasPeerId) + .GroupBy( + a => a.PeerId, + a => a, + (key, g) => new Peer {Id = key, Addresses = g.ToList()}); + foreach (var peer in peers) + try + { + PeerDiscovered?.Invoke(this, peer); + } + catch (Exception e) + { + _log.Error(e); + } + + return Task.CompletedTask; + } + + /// + public Task StopAsync() + { + _log.Debug("Stopping"); + PeerDiscovered = null; + return Task.CompletedTask; + } + } +} diff --git a/src/Lib.P2P/Discovery/IPeerDiscovery.cs b/src/Lib.P2P/Discovery/IPeerDiscovery.cs new file mode 100644 index 0000000000..a6000c1a52 --- /dev/null +++ b/src/Lib.P2P/Discovery/IPeerDiscovery.cs @@ -0,0 +1,47 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using MultiFormats; + +namespace Lib.P2P.Discovery +{ + /// + /// Describes a service that finds a peer. + /// + /// + /// All discovery services must raise the event. + /// + public interface IPeerDiscovery : IService + { + /// + /// Raised when a peer is discovered. + /// + /// + /// The peer must contain at least one . + /// The address must end with the ipfs protocol and the public ID + /// of the peer. For example "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" + /// + event EventHandler PeerDiscovered; + } +} diff --git a/src/Lib.P2P/Discovery/Mdns.cs b/src/Lib.P2P/Discovery/Mdns.cs new file mode 100644 index 0000000000..4af5564ba1 --- /dev/null +++ b/src/Lib.P2P/Discovery/Mdns.cs @@ -0,0 +1,160 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using Common.Logging; +using Makaretu.Dns; +using MultiFormats; + +namespace Lib.P2P.Discovery +{ + /// + /// Base class to discover peers using Multicast DNS. + /// + public abstract class Mdns : IPeerDiscovery + { + private static ILog _log = LogManager.GetLogger(typeof(Mdns)); + + /// + public event EventHandler PeerDiscovered; + + /// + /// The local peer. + /// + public Peer LocalPeer { get; set; } + + /// + /// The Muticast Domain Name Service to use. + /// + public MulticastService MulticastService { get; set; } + + /// + /// The service name for our peers. + /// + /// + /// Defaults to "ipfs". + /// + public string ServiceName { get; set; } = "ipfs"; + + /// + /// Determines if the local peer responds to a query. + /// + /// + /// true to answer queries. Defaults to true. + /// + public bool Broadcast { get; set; } = true; + + /// + public Task StartAsync() + { + MulticastService.NetworkInterfaceDiscovered += (s, e) => + { + try + { + var profile = BuildProfile(); + ServiceDiscovery discovery = new(MulticastService); + OnServiceDiscovery(discovery); + discovery.ServiceInstanceDiscovered += OnServiceInstanceDiscovered; + + if (Broadcast && profile != null) + { + _log.Debug($"Advertising {profile.FullyQualifiedName}"); + discovery.Advertise(profile); + } + + // Ask all peers to broadcast discovery info. + discovery.QueryServiceInstances(ServiceName); + } + catch (Exception ex) + { + _log.Debug("Failed to send query", ex); + + // eat it + } + }; + + return Task.CompletedTask; + } + + /// + public Task StopAsync() + { + PeerDiscovered = null; + return Task.CompletedTask; + } + + private void OnServiceInstanceDiscovered(object sender, ServiceInstanceDiscoveryEventArgs e) + { + try + { + var msg = e.Message; + + // Is it our service? + DomainName qsn = new(ServiceName + ".local"); + if (!e.ServiceInstanceName.BelongsTo(qsn)) + return; + + var addresses = GetAddresses(msg) + .Where(a => a.PeerId != LocalPeer.Id) + .ToArray(); + if (addresses.Length > 0) + PeerDiscovered?.Invoke(this, new Peer {Id = addresses[0].PeerId, Addresses = addresses}); + } + catch (Exception ex) + { + _log.Error("OnServiceInstanceDiscovered error", ex); + + // eat it + } + } + + /// + /// Build the profile which contains the DNS records that are needed + /// to locate and connect to the local peer. + /// + /// + /// Describes the service. + /// + public abstract ServiceProfile BuildProfile(); + + /// + /// Get the addresses of the peer in the DNS message. + /// + /// + /// An answer describing a peer. + /// + /// + /// All the addresses of the peer. + /// + public abstract IEnumerable GetAddresses(Message message); + + /// + /// Allows derived class to modify the service discovery behavior. + /// + /// + protected virtual void OnServiceDiscovery(ServiceDiscovery discovery) { } + } +} diff --git a/src/Lib.P2P/Discovery/MdnsGo.cs b/src/Lib.P2P/Discovery/MdnsGo.cs new file mode 100644 index 0000000000..36df6d730f --- /dev/null +++ b/src/Lib.P2P/Discovery/MdnsGo.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Lib.P2P.Discovery +{ + /// + /// Discovers peers using Multicast DNS according to + /// go-ipfs v0.4.17 + /// + /// + /// GO peers are not using the mDNS multicast address (224.0.0.251) + /// . + /// Basically this cannot work until the issue is resolved. + /// + public class MdnsGo : MdnsJs + { + /// + /// MDNS go is the same as MdnsJs except that the + /// service name is "_ipfs-discovery._udp". + /// + public MdnsGo() { ServiceName = "_ipfs-discovery._udp"; } + } +} diff --git a/src/Lib.P2P/Discovery/MdnsJs.cs b/src/Lib.P2P/Discovery/MdnsJs.cs new file mode 100644 index 0000000000..7315f6dc8f --- /dev/null +++ b/src/Lib.P2P/Discovery/MdnsJs.cs @@ -0,0 +1,108 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Linq; +using System.Net; +using Makaretu.Dns; +using MultiFormats; + +namespace Lib.P2P.Discovery +{ + /// + /// Discovers peers using Multicast DNS according to + /// js-ipfs v0.32.3 + /// + public class MdnsJs : Mdns + { + /// + /// Creates a new instance of the class. Sets the + /// to "ipfs". + /// + public MdnsJs() { ServiceName = "ipfs"; } + + /// + protected override void OnServiceDiscovery(ServiceDiscovery discovery) + { + discovery.AnswersContainsAdditionalRecords = true; + } + + /// + public override ServiceProfile BuildProfile() + { + // Only internet addresses. + var addresses = LocalPeer.Addresses + .Where(a => a.Protocols.First().Name == "ip4" || a.Protocols.First().Name == "ip6") + .ToArray(); + if (addresses.Length == 0) return null; + var ipAddresses = addresses + .Select(a => IPAddress.Parse(a.Protocols.First().Value)); + + // Only one port is supported. + var tcpPort = addresses.First() + .Protocols.First(p => p.Name == "tcp") + .Value; + + // Create the DNS records for this peer. The TXT record + // is singular and must contain the peer ID. + ServiceProfile profile = new( + LocalPeer.Id.ToBase58(), + ServiceName, + ushort.Parse(tcpPort), + ipAddresses + ); + profile.Resources.RemoveAll(r => r.Type == DnsType.TXT); + var txt = new TXTRecord {Name = profile.FullyQualifiedName}; + txt.Strings.Add(profile.InstanceName.ToString()); + profile.Resources.Add(txt); + + return profile; + } + + /// + public override IEnumerable GetAddresses(Message message) + { + var qsn = ServiceName + ".local"; + var peerNames = message.Answers + .OfType() + .Where(a => a.Name == qsn) + .Select(a => a.DomainName); + foreach (var name in peerNames) + { + var id = name.Labels[0]; + var srv = message.Answers + .OfType() + .First(r => r.Name == name); + var aRecords = message.Answers + .OfType() + .Where(a => a.Name == name || a.Name == srv.Target); + foreach (var a in aRecords) yield return new MultiAddress($"/ip4/{a.Address}/tcp/{srv.Port}/ipfs/{id}"); + var aaaaRecords = message.Answers + .OfType() + .Where(a => a.Name == name || a.Name == srv.Target); + foreach (var a in aaaaRecords) + yield return new MultiAddress($"/ip6/{a.Address}/tcp/{srv.Port}/ipfs/{id}"); + } + } + } +} diff --git a/src/Lib.P2P/Discovery/MdnsNext.cs b/src/Lib.P2P/Discovery/MdnsNext.cs new file mode 100644 index 0000000000..898e55f5f1 --- /dev/null +++ b/src/Lib.P2P/Discovery/MdnsNext.cs @@ -0,0 +1,100 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Makaretu.Dns; +using MultiFormats; + +namespace Lib.P2P.Discovery +{ + /// + /// Discovers peers using Multicast DNS according to + /// + /// + public class MdnsNext : Mdns + { + /// + /// Creates a new instance of the class. Sets the + /// to "_p2p._udp". + /// + public MdnsNext() { ServiceName = "_p2p._udp"; } + + /// + public override ServiceProfile BuildProfile() + { + ServiceProfile profile = new( + SafeLabel(LocalPeer.Id.ToBase32()), + ServiceName, + 0 + ); + + // The TXT records contain the multi addresses. + profile.Resources.RemoveAll(r => r is TXTRecord); + foreach (var address in LocalPeer.Addresses) + profile.Resources.Add(new TXTRecord + { + Name = profile.FullyQualifiedName, + Strings = {$"dnsaddr={address}"} + }); + + return profile; + } + + /// + public override IEnumerable GetAddresses(Message message) + { + return message.AdditionalRecords + .OfType() + .SelectMany(t => t.Strings) + .Where(s => s.StartsWith("dnsaddr=")) + .Select(s => s.Substring(8)) + .Select(MultiAddress.TryCreate) + .Where(a => a != null); + } + + /// + /// Creates a safe DNS label. + /// + /// + /// + /// + public static string SafeLabel(string label, int maxLength = 63) + { + if (label.Length <= maxLength) + return label; + + StringBuilder sb = new(); + while (label.Length > maxLength) + { + sb.Append(label.Substring(0, maxLength)); + sb.Append('.'); + label = label.Substring(maxLength); + } + + sb.Append(label); + return sb.ToString(); + } + } +} diff --git a/src/Lib.P2P/DuplexBufferedStream.cs b/src/Lib.P2P/DuplexBufferedStream.cs new file mode 100644 index 0000000000..e20aa83989 --- /dev/null +++ b/src/Lib.P2P/DuplexBufferedStream.cs @@ -0,0 +1,106 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +// BufferedStream is not available in Net Stardard 1.4 + +using System.Threading; +using System.Threading.Tasks; +#if !NETSTANDARD14 + +// Part of JuiceStream: https://juicestream.machinezoo.com +using System; +using System.IO; + +namespace Lib.P2P +{ + /// + /// .NET already has its BufferedStream, but that one will throw unexpected exceptions, especially on NetworkStreams. + /// JuiceStream's DuplexBufferedStream embeds two BufferedStream instances, + /// one for each direction, to provide full duplex buffering over non-seekable streams. + /// + /// + /// Copied from + /// + internal class DuplexBufferedStream : Stream + { + private readonly Stream _inner; + private readonly BufferedStream _readBuffer; + private readonly BufferedStream _writeBuffer; + + public override bool CanRead => _inner.CanRead; + public override bool CanSeek => false; + public override bool CanWrite => _inner.CanWrite; + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public DuplexBufferedStream(Stream stream) + { + _inner = stream; + _readBuffer = new BufferedStream(stream); + _writeBuffer = new BufferedStream(stream); + } + + protected override void Dispose(bool disposing) + { + if (disposing) + { + _writeBuffer.Flush(); + _inner.Dispose(); + _readBuffer.Dispose(); + _writeBuffer.Dispose(); + } + } + + public override void Flush() { _writeBuffer.Flush(); } + public override Task FlushAsync(CancellationToken token) { return _writeBuffer.FlushAsync(token); } + + public override int Read(byte[] buffer, int offset, int count) + { + return _readBuffer.Read(buffer, offset, count); + } + + public override Task ReadAsync(byte[] buffer, int offset, int count, CancellationToken token) + { + return _readBuffer.ReadAsync(buffer, offset, count, token); + } + + public override int ReadByte() { return _readBuffer.ReadByte(); } + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + public override void SetLength(long value) { throw new NotSupportedException(); } + public override void Write(byte[] buffer, int offset, int count) { _writeBuffer.Write(buffer, offset, count); } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken token) + { + return _writeBuffer.WriteAsync(buffer, offset, count, token); + } + + public override void WriteByte(byte value) { _writeBuffer.WriteByte(value); } + } +} + +#endif diff --git a/src/Lib.P2P/IDataBlock.cs b/src/Lib.P2P/IDataBlock.cs new file mode 100644 index 0000000000..f377e85505 --- /dev/null +++ b/src/Lib.P2P/IDataBlock.cs @@ -0,0 +1,78 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; + +namespace Lib.P2P +{ + /// + /// represents some data dfs + /// + /// + /// A DataBlock has an unique ID + /// and some data ( + /// or ). + /// + /// It is useful to talk about them as "blocks" in Bitswap + /// and other things that do not care about what is being stored. + /// + /// + /// + /// Catalyst.Ipfs.Core.IMerkleNode{Link} + /// + public interface IDataBlock + { + /// + /// Contents as a byte array. + /// + /// + /// It is never null. + /// + /// + /// The contents as a sequence of bytes. + /// + byte[] DataBytes { get; } + + /// + /// Contents as a stream of bytes. + /// + /// + /// The contents as a stream of bytes. + /// + Stream DataStream { get; } + + /// + /// The unique ID of the data. + /// + /// + /// A of the content. + /// + Cid Id { get; } + + /// + /// The size (in bytes) of the data. + /// + /// Number of bytes. + long Size { get; } + } +} diff --git a/src/Lib.P2P/INetworkProtector.cs b/src/Lib.P2P/INetworkProtector.cs new file mode 100644 index 0000000000..3750384b10 --- /dev/null +++ b/src/Lib.P2P/INetworkProtector.cs @@ -0,0 +1,63 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Lib.P2P +{ + /// + /// Provides access to a private network of peers. + /// + /// + /// The calls the network protector whenever a connection + /// is being established with another peer. + /// + /// + public interface INetworkProtector + { + /// + /// Creates a protected stream for the connection. + /// + /// + /// A connection between two peers. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the protected stream. + /// + /// + /// ProtectAsync is called after the transport level has established + /// the connection. + /// + /// An exception is thrown if the remote peer is not a member of + /// the private network. + /// + /// + Task ProtectAsync(PeerConnection connection, CancellationToken cancel = default); + } +} diff --git a/src/Lib.P2P/IPolicy.cs b/src/Lib.P2P/IPolicy.cs new file mode 100644 index 0000000000..3c5699c933 --- /dev/null +++ b/src/Lib.P2P/IPolicy.cs @@ -0,0 +1,46 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Lib.P2P +{ + /// + /// A rule that must be enforced. + /// + /// + /// The type of object that the rule applies to. + /// + public interface IPolicy + { + /// + /// Determines if the target passes the rule. + /// + /// + /// An object to test against the rule. + /// + /// + /// true if the passes the rule; + /// otherwise false. + /// + bool IsAllowed(T target); + } +} diff --git a/src/Lib.P2P/IService.cs b/src/Lib.P2P/IService.cs new file mode 100644 index 0000000000..d08fb80229 --- /dev/null +++ b/src/Lib.P2P/IService.cs @@ -0,0 +1,43 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading.Tasks; + +namespace Lib.P2P +{ + /// + /// A service is async and can be started and stopped. + /// + public interface IService + { + /// + /// Start the service. + /// + Task StartAsync(); + + /// + /// Stop the service. + /// + Task StopAsync(); + } +} diff --git a/src/Lib.P2P/ISwarmService.cs b/src/Lib.P2P/ISwarmService.cs new file mode 100644 index 0000000000..da578e3843 --- /dev/null +++ b/src/Lib.P2P/ISwarmService.cs @@ -0,0 +1,394 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Cryptography; +using Lib.P2P.Protocols; +using Lib.P2P.Routing; +using MultiFormats; + +namespace Lib.P2P +{ + /// + /// + /// + public interface ISwarmService : IService, IPolicy, IPolicy + { + /// + /// Raised when a listener is establihed. + /// + /// + /// Raised when + /// succeeds. + /// + event EventHandler ListenerEstablished; + + /// + /// Raised when a connection to another peer is established. + /// + event EventHandler ConnectionEstablished; + + /// + /// Raised when a new peer is discovered for the first time. + /// + event EventHandler PeerDiscovered; + + /// + /// Raised when a peer's connection is closed. + /// + event EventHandler PeerDisconnected; + + /// + /// Raised when a peer should no longer be used. + /// + /// + /// This event indicates that the peer has been removed + /// from the and should no longer + /// be used. + /// + event EventHandler PeerRemoved; + + /// + /// Raised when a peer cannot be connected to. + /// + event EventHandler PeerNotReachable; + + /// + /// The local peer. + /// + /// + /// The local peer must have an and + /// . + /// + Peer LocalPeer { get; set; } + + /// + /// The private key of the local peer. + /// + /// + /// Used to prove the identity of the . + /// + Key LocalPeerKey { get; set; } + + /// + /// Use to find addresses of a peer. + /// + IPeerRouting Router { get; set; } + + /// + /// Provides access to a private network of peers. + /// + INetworkProtector NetworkProtector { get; set; } + + /// + /// Determines if the swarm has been started. + /// + /// + /// true if the swarm has started; otherwise, false. + /// + bool IsRunning { get; } + + /// + /// Get the sequence of all known peer addresses. + /// + /// + /// Contains any peer address that has been + /// discovered. + /// + /// + IEnumerable KnownPeerAddresses { get; } + + /// + /// Get the sequence of all known peers. + /// + /// + /// Contains any peer that has been + /// discovered. + /// + /// + IEnumerable KnownPeers { get; } + + /// + /// The addresses that cannot be used. + /// + MultiAddressBlackList BlackList { get; set; } + + /// + /// The addresses that can be used. + /// + MultiAddressWhiteList WhiteList { get; set; } + + ConnectionManager Manager { get; } + + /// + /// Register that a peer's address has been discovered. + /// + /// + /// An address to the peer. It must end with the peer ID. + /// + /// + /// The that is registered. + /// + /// + /// The or policies forbid it. + /// Or the "p2p/ipfs" protocol name is missing. + /// + /// + /// If the is not already known, then it is + /// added to the . + /// + /// + Peer RegisterPeerAddress(MultiAddress address); + + /// + /// Register that a peer has been discovered. + /// + /// + /// The newly discovered peer. + /// + /// + /// The registered peer. + /// + /// + /// If the peer already exists, then the existing peer is updated with supplied + /// information and is then returned. Otherwise, the + /// is added to known peers and is returned. + /// + /// If the peer already exists, then a union of the existing and new addresses + /// is used. For all other information the 's information + /// is used if not null. + /// + /// + /// If peer does not already exist, then the event + /// is raised. + /// + /// + /// + /// The or policies forbid it. + /// + Peer RegisterPeer(Peer peer); + + /// + /// Register that a peer has been discovered. + /// + /// + /// The newly discovered peer. + /// + /// + /// Ignores the blacklist or whitelist restrictions. + /// + /// + /// The registered peer. + /// + /// + /// If the peer already exists, then the existing peer is updated with supplied + /// information and is then returned. Otherwise, the + /// is added to known peers and is returned. + /// + /// If the peer already exists, then a union of the existing and new addresses + /// is used. For all other information the 's information + /// is used if not null. + /// + /// + /// If peer does not already exist, then the event + /// is raised. + /// + /// + /// + /// The or policies forbid it. + /// + Peer RegisterPeer(Peer peer, bool ignoreRestrictionLists); + + /// + /// Deregister a peer. + /// + /// + /// The peer to remove.. + /// + /// + /// Remove all knowledge of the peer. The event + /// is raised. + /// + void DeregisterPeer(Peer peer); + + /// + /// Determines if a connection is being made to the peer. + /// + /// + /// A . + /// + /// + /// true is the has a pending connection. + /// + bool HasPendingConnection(Peer peer); + + /// + /// Connect to a peer using the specified . + /// + /// + /// An ipfs , such as + /// /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the . + /// + /// + /// If already connected to the peer and is active on any address, then + /// the existing connection is returned. + /// + Task ConnectAsync(MultiAddress address, + CancellationToken cancel = default); + + /// + /// Connect to a peer. + /// + /// + /// A peer to connect to. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the . + /// + /// + /// If already connected to the peer and is active on any address, then + /// the existing connection is returned. + /// + Task ConnectAsync(Peer peer, CancellationToken cancel = default); + + /// + /// Create a stream to the peer that talks the specified protocol. + /// + /// + /// The remote peer. + /// + /// + /// The protocol name, such as "/foo/0.42.0". + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the new to the . + /// + /// + /// + /// When finished, the caller must the + /// new stream. + /// + /// + Task DialAsync(Peer peer, + string protocol, + CancellationToken cancel = default); + + /// + /// Disconnect from a peer. + /// + /// + /// An ipfs , such as + /// /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// If the peer is not conected, then nothing happens. + /// + Task DisconnectAsync(MultiAddress address, CancellationToken cancel = default); + + /// + /// Start listening on the specified . + /// + /// + /// Typically "/ip4/0.0.0.0/tcp/4001" or "/ip6/::/tcp/4001". + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is a than can be used by another peer + /// to connect to tis peer. + /// + /// + /// Already listening on . + /// + /// + /// is missing a transport protocol (such as tcp or udp). + /// + /// + /// Allows other peers to connect + /// to the . + /// + /// The of the are updated. If the refers to + /// any IP address ("/ip4/0.0.0.0" or "/ip6/::") then all network interfaces addresses + /// are added. If the port is zero (as in "/ip6/::/tcp/0"), then the peer addresses contains the actual port number + /// that was assigned. + /// + /// + Task StartListeningAsync(MultiAddress address); + + /// + /// Add a protocol that is supported by the swarm. + /// + /// + /// The protocol to add. + /// + void AddProtocol(IPeerProtocol protocol); + + /// + /// Remove a protocol from the swarm. + /// + /// + /// The protocol to remove. + /// + void RemoveProtocol(IPeerProtocol protocol); + + /// + /// Stop listening on the specified . + /// + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// Allows other peers to connect + /// to the . + /// + /// The addresses of the are updated. + /// + /// + Task StopListeningAsync(MultiAddress address); + } +} diff --git a/src/Lib.P2P/Lib.P2P.csproj b/src/Lib.P2P/Lib.P2P.csproj new file mode 100644 index 0000000000..501a911f3c --- /dev/null +++ b/src/Lib.P2P/Lib.P2P.csproj @@ -0,0 +1,31 @@ + + + net6.0 + Lib.P2P + Darren Priestnall (darren.op@catalystnet.org) + true + Lib.P2P.snk + true + false + + + + + + + + + + + + + + + + + + + + + + diff --git a/src/Lib.P2P/Lib.P2P.snk b/src/Lib.P2P/Lib.P2P.snk new file mode 100644 index 0000000000..e364356b76 Binary files /dev/null and b/src/Lib.P2P/Lib.P2P.snk differ diff --git a/src/Lib.P2P/MessageTracker.cs b/src/Lib.P2P/MessageTracker.cs new file mode 100644 index 0000000000..028091a033 --- /dev/null +++ b/src/Lib.P2P/MessageTracker.cs @@ -0,0 +1,103 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Linq; + +namespace Lib.P2P +{ + /// + /// Maintains a timed cache of message IDs. + /// + /// + /// can be used to detect duplicate + /// messages based on its ID. + /// + public class MessageTracker + { + /// + /// The tracked messages. + /// + /// + /// The key is the ID of a message. The value is the expiry date. + /// + private ConcurrentDictionary _messages = new(); + + /// + /// The definition of recent. + /// + /// + /// Defaults to 10 minutes. + /// + /// + /// Messages that have been in the cache longer that this value + /// will be removed and then be considered as "not seen". + /// + public TimeSpan Recent { get; set; } = TimeSpan.FromMinutes(10); + + /// + /// Determines if the message has recently been seen. + /// + /// + /// The unique identifier of a message. + /// + /// + /// + /// true if the has been recently seen; + /// otherwise, false. + /// + public bool RecentlySeen(string id, DateTime? now = null) + { + now = now ?? DateTime.Now; + Prune(now.Value); + + var seen = false; + _messages.AddOrUpdate( + id, + (key) => now.Value + Recent, + (key, expiry) => + { + seen = true; + return now.Value + Recent; + }); + + return seen; + } + + /// + /// Removes any message that is past its expiry date. + /// + /// + /// The current clock time. + /// + public void Prune(DateTime now) + { + var expired = _messages + .Where(e => now >= e.Value) + .Select(e => e.Key) + .ToArray(); + foreach (var key in expired) _messages.TryRemove(key, out _); + } + } +} diff --git a/src/Lib.P2P/MultiAddressBlackList.cs b/src/Lib.P2P/MultiAddressBlackList.cs new file mode 100644 index 0000000000..d139b0cf01 --- /dev/null +++ b/src/Lib.P2P/MultiAddressBlackList.cs @@ -0,0 +1,80 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using MultiFormats; + +namespace Lib.P2P +{ + /// + /// A collection of filters that are not approved. + /// + /// + /// Only targets that do match a filter will pass. + /// + public class MultiAddressBlackList : ICollection, IPolicy + { + private ConcurrentDictionary _filters = + new(); + + /// + public bool IsAllowed(MultiAddress target) { return !_filters.Any(kvp => Matches(kvp.Key, target)); } + + private bool Matches(MultiAddress filter, MultiAddress target) + { + return filter + .Protocols + .All(fp => target.Protocols.Any(tp => tp.Code == fp.Code && tp.Value == fp.Value)); + } + + /// + public bool Remove(MultiAddress item) { return _filters.TryRemove(item, out _); } + + /// + public int Count => _filters.Count; + + /// + public bool IsReadOnly => false; + + /// + public void Add(MultiAddress item) { _filters.TryAdd(item, item); } + + /// + public void Clear() { _filters.Clear(); } + + /// + public bool Contains(MultiAddress item) { return _filters.Keys.Contains(item); } + + /// + public void CopyTo(MultiAddress[] array, int arrayIndex) { _filters.Keys.CopyTo(array, arrayIndex); } + + /// + public IEnumerator GetEnumerator() { return _filters.Keys.GetEnumerator(); } + + /// + IEnumerator IEnumerable.GetEnumerator() { return _filters.Keys.GetEnumerator(); } + } +} diff --git a/src/Lib.P2P/MultiAddressExtensions.cs b/src/Lib.P2P/MultiAddressExtensions.cs new file mode 100644 index 0000000000..a917c547a6 --- /dev/null +++ b/src/Lib.P2P/MultiAddressExtensions.cs @@ -0,0 +1,147 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using MultiFormats; + +namespace Lib.P2P +{ + /// + /// Extensions to . + /// + public static class MultiAddressExtensions + { + private static Dictionary _supportedDnsAddressFamilies = + new(); + + private static MultiAddress _http = new("/tcp/80"); + private static MultiAddress _https = new("/tcp/443"); + + static MultiAddressExtensions() + { + if (Socket.OSSupportsIPv4) + _supportedDnsAddressFamilies[AddressFamily.InterNetwork] = "/ip4/"; + if (Socket.OSSupportsIPv6) + _supportedDnsAddressFamilies[AddressFamily.InterNetworkV6] = "/ip6/"; + } + + /// + /// Determines if the multiaddress references + /// a loopback address. + /// + /// + /// The mutiaddress to clone. + /// + /// + /// true for a loopback (127.0.0.1 or ::1). + /// + public static bool IsLoopback(this MultiAddress multiaddress) + { + return multiaddress.Protocols.Any(p => + p.Name == "ip4" && p.Value == "127.0.0.1" || + p.Name == "ip6" && p.Value == "::1"); + } + + /// + /// Get all the addresses for the specified . + /// + /// + /// The multiaddress to resolve. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is a sequence of possible multiaddresses. + /// + /// + /// The host name cannot be resolved. + /// + /// + /// When the starts with "dns", then a DNS + /// lookup is performed to get all the IP addresses for the host name. "dn4" and "dns6" + /// will filter the result for IPv4 and IPV6 addresses. + /// + /// When the is "http" or "https", then + /// a "tcp/80" or "tcp/443" is respectively added. + /// + /// + public static async Task> ResolveAsync(this MultiAddress multiaddress, + CancellationToken cancel = default) + { + List list = new(); + + // HTTP + var i = multiaddress.Protocols.FindIndex(ma => ma.Name == "http"); + if (i >= 0 && !multiaddress.Protocols.Any(p => p.Name == "tcp")) + { + multiaddress = multiaddress.Clone(); + multiaddress.Protocols.InsertRange(i + 1, _http.Protocols); + } + + // HTTPS + i = multiaddress.Protocols.FindIndex(ma => ma.Name == "https"); + if (i >= 0 && !multiaddress.Protocols.Any(p => p.Name == "tcp")) + { + multiaddress = multiaddress.Clone(); + multiaddress.Protocols.InsertRange(i + 1, _https.Protocols); + } + + // DNS* + i = multiaddress.Protocols.FindIndex(ma => ma.Name.StartsWith("dns")); + if (i < 0) + { + list.Add(multiaddress); + return list; + } + + var protocolName = multiaddress.Protocols[i].Name; + var host = multiaddress.Protocols[i].Value; + + // TODO: Don't use DNS, but use the IPFS Engine DNS resolver. + // This will not then expose the domain name in plain text. + // We also, then get to specify if A and/or AAAA records are needed. + var addresses = (await Dns.GetHostAddressesAsync(host).ConfigureAwait(false)) + .Where(a => _supportedDnsAddressFamilies.ContainsKey(a.AddressFamily)) + .Where(a => + protocolName == "dns" || + protocolName == "dns4" && a.AddressFamily == AddressFamily.InterNetwork || + protocolName == "dns6" && a.AddressFamily == AddressFamily.InterNetworkV6); + foreach (var addr in addresses) + { + MultiAddress ma0 = new(_supportedDnsAddressFamilies[addr.AddressFamily] + addr); + var ma1 = multiaddress.Clone(); + ma1.Protocols[i] = ma0.Protocols[0]; + list.Add(ma1); + } + + return list; + } + } +} diff --git a/src/Lib.P2P/MultiAddressWhiteList.cs b/src/Lib.P2P/MultiAddressWhiteList.cs new file mode 100644 index 0000000000..0d6c3bf5fa --- /dev/null +++ b/src/Lib.P2P/MultiAddressWhiteList.cs @@ -0,0 +1,87 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using MultiFormats; + +namespace Lib.P2P +{ + /// + /// A sequence of filters that are approved. + /// + /// + /// Only targets that are a subset of any filters will pass. If no filters are defined, then anything + /// passes. + /// + public class MultiAddressWhiteList : ICollection, IPolicy + { + private ConcurrentDictionary _filters = + new(); + + /// + public bool IsAllowed(MultiAddress target) + { + if (_filters.IsEmpty) + return true; + + return _filters.Any(kvp => Matches(kvp.Key, target)); + } + + private bool Matches(MultiAddress filter, MultiAddress target) + { + return filter + .Protocols + .All(fp => target.Protocols.Any(tp => tp.Code == fp.Code && tp.Value == fp.Value)); + } + + /// + public bool Remove(MultiAddress item) { return _filters.TryRemove(item, out _); } + + /// + public int Count => _filters.Count; + + /// + public bool IsReadOnly => false; + + /// + public void Add(MultiAddress item) { _filters.TryAdd(item, item); } + + /// + public void Clear() { _filters.Clear(); } + + /// + public bool Contains(MultiAddress item) { return _filters.Keys.Contains(item); } + + /// + public void CopyTo(MultiAddress[] array, int arrayIndex) { _filters.Keys.CopyTo(array, arrayIndex); } + + /// + public IEnumerator GetEnumerator() { return _filters.Keys.GetEnumerator(); } + + /// + IEnumerator IEnumerable.GetEnumerator() { return _filters.Keys.GetEnumerator(); } + } +} diff --git a/src/Lib.P2P/Multiplex/Header.cs b/src/Lib.P2P/Multiplex/Header.cs new file mode 100644 index 0000000000..c0e7666dc6 --- /dev/null +++ b/src/Lib.P2P/Multiplex/Header.cs @@ -0,0 +1,114 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MultiFormats; + +namespace Lib.P2P.Multiplex +{ + /// + /// The header of a multiplex message. + /// + /// + /// The header of a multiplex message contains the and + /// encoded as a variable integer. + /// + /// + public struct Header + { + /// + /// The largest possible value of a . + /// + /// + /// long.MaxValue >> 3. + /// + public const long MaxStreamId = long.MaxValue >> 3; + + /// + /// The smallest possible value of a . + /// + /// + /// Zero. + /// + public const long MinStreamId = 0; + + /// + /// The stream identifier. + /// + /// + /// The session initiator allocates odd IDs and the session receiver allocates even IDs. + /// + public long StreamId; + + /// + /// The purpose of the multiplex message. + /// + /// + /// One of the enumeration values. + /// + public PacketType PacketType; + + /// + /// Writes the header to the specified . + /// + /// + /// The destination for the header. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + public async Task WriteAsync(Stream stream, CancellationToken cancel = default) + { + var header = (StreamId << 3) | (long) PacketType; + await Varint.WriteVarintAsync(stream, header, cancel).ConfigureAwait(false); + } + + /// + /// Reads the header from the specified . + /// + /// + /// The source for the header. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the decoded . + /// + public static async Task
ReadAsync(Stream stream, CancellationToken cancel = default) + { + var varint = await Varint.ReadVarint64Async(stream, cancel).ConfigureAwait(false); + return new Header + { + StreamId = varint >> 3, + PacketType = (PacketType) ((byte) varint & 0x7) + }; + } + } +} diff --git a/src/Lib.P2P/Multiplex/Muxer.cs b/src/Lib.P2P/Multiplex/Muxer.cs new file mode 100644 index 0000000000..9b461a2109 --- /dev/null +++ b/src/Lib.P2P/Multiplex/Muxer.cs @@ -0,0 +1,340 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.IO; +using System.Linq; +using System.Net.Sockets; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using MultiFormats; +using Nito.AsyncEx; + +namespace Lib.P2P.Multiplex +{ + /// + /// Supports multiple protocols over a single channel (stream). + /// + /// + /// See for the spec. + /// + public class Muxer + { + private static ILog _log = LogManager.GetLogger(typeof(Muxer)); + + /// + /// The next stream ID to create. + /// + /// + /// The session initiator allocates even IDs and the session receiver allocates odd IDs. + /// + public long NextStreamId { get; private set; } = 1000; + + /// + /// The signle channel to exchange protocol messages. + /// + /// + /// A to exchange protocol messages. + /// + public Stream Channel { get; set; } + + /// + /// The peer connection. + /// + /// + /// The peer connection that owns this muxer. + /// + public PeerConnection Connection { get; set; } + + /// + /// Raised when the remote end creates a new stream. + /// + public event EventHandler SubstreamCreated; + + /// + /// Raised when the remote end closes a stream. + /// + public event EventHandler SubstreamClosed; + + private readonly AsyncLock _channelWriteLock = new(); + + /// + /// The substreams that are open. + /// + /// + /// The key is stream ID and the value is a . + /// + public ConcurrentDictionary Substreams = new(); + + /// + /// Determines if the muxer is the initiator. + /// + /// + /// true if the muxer is the initiator. + /// + /// + public bool Initiator + { + get => (NextStreamId & 1) == 0; + set + { + if (value != Initiator) + NextStreamId += 1; + } + } + + /// + /// Determines if the muxer is the receiver. + /// + /// + /// true if the muxer is the receiver. + /// + /// + public bool Receiver { get => !Initiator; set => Initiator = !value; } + + /// + /// Creates a new stream with the specified name. + /// + /// + /// A name for the stream. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A duplex stream. + /// + public async Task CreateStreamAsync(string name = "", + CancellationToken cancel = default) + { + var streamId = NextStreamId; + NextStreamId += 2; + var substream = new Substream + { + Id = streamId, + Name = name, + Muxer = this, + SentMessageType = PacketType.MessageInitiator, + }; + Substreams.TryAdd(streamId, substream); + + // Tell the other side about the new stream. + using (await AcquireWriteAccessAsync().ConfigureAwait(false)) + { + var header = new Header {StreamId = streamId, PacketType = PacketType.NewStream}; + var wireName = Encoding.UTF8.GetBytes(name); + await header.WriteAsync(Channel, cancel).ConfigureAwait(false); + await Channel.WriteVarintAsync(wireName.Length, cancel).ConfigureAwait(false); + await Channel.WriteAsync(wireName, 0, wireName.Length, cancel).ConfigureAwait(false); + await Channel.FlushAsync(cancel).ConfigureAwait(false); + } + + return substream; + } + + /// + /// Remove the stream. + /// + /// + /// Internal method called by Substream.Dispose(). + /// + public async Task RemoveStreamAsync(Substream stream, + CancellationToken cancel = default) + { + if (Substreams.TryRemove(stream.Id, out _)) + + // Tell the other side. + using (await AcquireWriteAccessAsync().ConfigureAwait(false)) + { + var header = new Header + { + StreamId = stream.Id, + PacketType = PacketType.CloseInitiator + }; + await header.WriteAsync(Channel, cancel).ConfigureAwait(false); + Channel.WriteByte(0); // length + await Channel.FlushAsync(cancel).ConfigureAwait(false); + } + + return stream; + } + + /// + /// Read the multiplex packets. + /// + /// + /// + /// + /// A background task that reads and processes the multiplex packets while + /// the is open and not cancelled. + /// + /// Any encountered errors will close the . + /// + /// + public async Task ProcessRequestsAsync(CancellationToken cancel = default) + { + try + { + while (Channel.CanRead && !cancel.IsCancellationRequested) + { + // Read the packet prefix. + var header = await Header.ReadAsync(Channel, cancel).ConfigureAwait(false); + var length = await Varint.ReadVarint32Async(Channel, cancel).ConfigureAwait(false); + if (_log.IsTraceEnabled) + _log.TraceFormat("received '{0}', stream={1}, length={2}", header.PacketType, header.StreamId, + length); + + // Read the payload. + var payload = new byte[length]; + await Channel.ReadExactAsync(payload, 0, length, cancel).ConfigureAwait(false); + + // Process the packet + Substreams.TryGetValue(header.StreamId, out var substream); + switch (header.PacketType) + { + case PacketType.NewStream: + if (substream != null) + { + _log.Warn($"Stream {substream.Id} already exists"); + continue; + } + + substream = new Substream + { + Id = header.StreamId, + Name = Encoding.UTF8.GetString(payload), + Muxer = this + }; + if (!Substreams.TryAdd(substream.Id, substream)) + + // Should not happen. + throw new Exception($"Stream {substream.Id} already exists"); + SubstreamCreated?.Invoke(this, substream); + + // Special hack for go-ipfs +#if true + if (Receiver && (substream.Id & 1) == 1) + { + _log.Debug($"go-hack sending newstream {substream.Id}"); + using (await AcquireWriteAccessAsync().ConfigureAwait(false)) + { + var hdr = new Header + { + StreamId = substream.Id, + PacketType = PacketType.NewStream + }; + await hdr.WriteAsync(Channel, cancel).ConfigureAwait(false); + Channel.WriteByte(0); // length + await Channel.FlushAsync(cancel).ConfigureAwait(false); + } + } +#endif + break; + + case PacketType.MessageInitiator: + if (substream == null) + { + _log.Warn($"Message to unknown stream #{header.StreamId}"); + continue; + } + + substream.AddData(payload); + break; + + case PacketType.MessageReceiver: + if (substream == null) + { + _log.Warn($"Message to unknown stream #{header.StreamId}"); + continue; + } + + substream.AddData(payload); + break; + + case PacketType.CloseInitiator: + case PacketType.CloseReceiver: + case PacketType.ResetInitiator: + case PacketType.ResetReceiver: + if (substream == null) + { + _log.Warn($"Reset of unknown stream #{header.StreamId}"); + continue; + } + + substream.NoMoreData(); + Substreams.TryRemove(substream.Id, out _); + SubstreamClosed?.Invoke(this, substream); + break; + + default: + throw new InvalidDataException($"Unknown Muxer packet type '{header.PacketType}'."); + } + } + } + catch (EndOfStreamException) + { + // eat it + } + catch (IOException) + { + // eat it + } + catch (SocketException e) when (e.SocketErrorCode == SocketError.ConnectionReset) + { + // eat it + } + catch (Exception) when (cancel.IsCancellationRequested) + { + // eat it + } + catch (Exception e) + { + // Log error if the channel is not closed. + if (Channel.CanRead || Channel.CanWrite) _log.Error("failed", e); + } + + // Some of the tests do not pass a connection. + if (Connection != null) + Connection.Dispose(); + else if (Channel != null) + await Channel.DisposeAsync(); + + // Dispose of all the substreams. + var streams = Substreams.Values.ToArray(); + Substreams.Clear(); + foreach (var stream in streams) stream?.DisposeAsync(); + } + + /// + /// Acquire permission to write to the Channel. + /// + /// + /// A task that represents the asynchronous get operation. The task's value + /// is an that releases the lock. + /// + public Task AcquireWriteAccessAsync() { return _channelWriteLock.LockAsync(); } + } +} diff --git a/src/Lib.P2P/Multiplex/PacketType.cs b/src/Lib.P2P/Multiplex/PacketType.cs new file mode 100644 index 0000000000..14039229a2 --- /dev/null +++ b/src/Lib.P2P/Multiplex/PacketType.cs @@ -0,0 +1,67 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Lib.P2P.Multiplex +{ + /// + /// The purpose of the multiplex message. + /// + /// + public enum PacketType : byte + { + /// + /// Create a new stream. + /// + NewStream = 0, + + /// + /// A message from the "receiver". + /// + MessageReceiver = 1, + + /// + /// A message from the "initiator". + /// + MessageInitiator = 2, + + /// + /// Close the stream from the "receiver". + /// + CloseReceiver = 3, + + /// + /// Close the stream from the "initiator". + /// + CloseInitiator = 4, + + /// + /// Reset the stream from the "receiver". + /// + ResetReceiver = 5, + + /// + /// Reset the stream from the "initiator". + /// + ResetInitiator = 6 + } +} diff --git a/src/Lib.P2P/Multiplex/Substream.cs b/src/Lib.P2P/Multiplex/Substream.cs new file mode 100644 index 0000000000..edf25b1cfa --- /dev/null +++ b/src/Lib.P2P/Multiplex/Substream.cs @@ -0,0 +1,240 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using System.Threading.Tasks.Dataflow; +using MultiFormats; + +namespace Lib.P2P.Multiplex +{ + /// + /// A duplex substream used by the . + /// + /// + /// Reading of data waits on the Muxer calling . + /// is used to signal the end of stream. + /// + /// Writing data is buffered until is + /// called. + /// + /// + public class Substream : Stream + { + private BufferBlock _inBlocks = new(); + private byte[] _inBlock; + private int _inBlockOffset; + private bool _eos; + + private Stream _outStream = new MemoryStream(); + + /// + /// The type of message of sent to the other side. + /// + /// + /// Either or . + /// Defaults to . + /// + public PacketType SentMessageType = PacketType.MessageReceiver; + + /// + /// The stream identifier. + /// + /// + /// The session initiator allocates odd IDs and the session receiver allocates even IDs. + /// + public long Id; + + /// + /// A name for the stream. + /// + /// + /// Names do not need to be unique. + /// + public string Name; + + /// + /// The multiplexor associated with the substream. + /// + public Muxer Muxer { get; set; } + + /// + public override bool CanRead => !_eos; + + /// + public override bool CanSeek => false; + + /// + public override bool CanWrite => _outStream != null; + + /// + public override bool CanTimeout => false; + + /// + public override long Length => throw new NotSupportedException(); + + /// + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + /// + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + + /// + public override void SetLength(long value) { throw new NotSupportedException(); } + + /// + /// Add some data that should be read by the stream. + /// + /// + /// The data to be read. + /// + /// + /// AddData is called when the muxer receives a packet for this + /// stream. + /// + public void AddData(byte[] data) { _inBlocks.Post(data); } + + /// + /// Indicates that the stream will not receive any more data. + /// + /// + /// + /// NoMoreData is called when the muxer receives a packet to + /// close this stream. + /// + public void NoMoreData() { _inBlocks.Complete(); } + + /// + public override int Read(byte[] buffer, int offset, int count) + { +#pragma warning disable VSTHRD002 + return ReadAsync(buffer, offset, count).GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 + } + + /// + public override async Task ReadAsync(byte[] buffer, + int offset, + int count, + CancellationToken cancellationToken) + { + var total = 0; + while (count > 0 && !_eos) + + // Does the current block have some unread data? + if (_inBlock != null && _inBlockOffset < _inBlock.Length) + { + var n = Math.Min(_inBlock.Length - _inBlockOffset, count); + Array.Copy(_inBlock, _inBlockOffset, buffer, offset, n); + total += n; + count -= n; + offset += n; + _inBlockOffset += n; + } + + // Otherwise, wait for a new block of data. + else + { + try + { + _inBlock = await _inBlocks.ReceiveAsync(cancellationToken).ConfigureAwait(false); + _inBlockOffset = 0; + } + catch (InvalidOperationException) // no more data! + { + _eos = true; + } + } + + return total; + } + + /// + public override void Flush() + { +#pragma warning disable VSTHRD002 + FlushAsync().GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 + } + + /// + public override async Task FlushAsync(CancellationToken cancel) + { + if (_outStream.Length == 0) + return; + + // Send the response over the muxer channel + using (await Muxer.AcquireWriteAccessAsync().ConfigureAwait(false)) + { + _outStream.Position = 0; + var header = new Header + { + StreamId = Id, + PacketType = SentMessageType + }; + await header.WriteAsync(Muxer.Channel, cancel).ConfigureAwait(false); + await Muxer.Channel.WriteVarintAsync(_outStream.Length, cancel).ConfigureAwait(false); + await _outStream.CopyToAsync(Muxer.Channel, cancel).ConfigureAwait(false); + await Muxer.Channel.FlushAsync(cancel).ConfigureAwait(false); + + _outStream.SetLength(0); + } + } + + /// + public override void Write(byte[] buffer, int offset, int count) { _outStream.Write(buffer, offset, count); } + + /// + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return _outStream.WriteAsync(buffer, offset, count, cancellationToken); + } + + /// + public override void WriteByte(byte value) { _outStream.WriteByte(value); } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + Muxer?.RemoveStreamAsync(this); + + _eos = true; + if (_outStream != null) + { + _outStream.Dispose(); + _outStream = null; + } + } + + base.Dispose(disposing); + } + } +} diff --git a/src/Lib.P2P/Peer.cs b/src/Lib.P2P/Peer.cs new file mode 100644 index 0000000000..a18638bb7d --- /dev/null +++ b/src/Lib.P2P/Peer.cs @@ -0,0 +1,186 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using MultiFormats; + +namespace Lib.P2P +{ + /// + /// A daemon node on the IPFS network. + /// + /// + /// Equality is based solely on the peer's . + /// + public class Peer : IEquatable + { + private static MultiAddress[] _noAddress = Array.Empty(); + private const string Unknown = "unknown/0.0"; + + /// + /// Universally unique identifier. + /// + /// + /// This is the of the peer's protobuf encoded + /// . + /// + /// + public MultiHash Id { get; set; } + + /// + /// The public key of the node. + /// + /// + /// The base 64 encoding of the node's public key. The default is null + /// + /// + /// The IPFS public key is the base-64 encoding of a protobuf encoding containing + /// a type and the DER encoding of the PKCS Subject Public Key Info. + /// + /// + public string PublicKey { get; set; } + + /// + /// The multiple addresses of the node. + /// + /// + /// Where the peer can be found. The default is an empty sequence. + /// + public IEnumerable Addresses { get; set; } = _noAddress; + + /// + /// The name and version of the IPFS software. + /// + /// + /// For example "go-ipfs/0.4.17/". + /// + /// + /// There is no specification that describes the agent version string. The default + /// is "unknown/0.0". + /// + public string AgentVersion { get; set; } = Unknown; + + /// + /// The name and version of the supported IPFS protocol. + /// + /// + /// For example "ipfs/0.1.0". + /// + /// + /// There is no specification that describes the protocol version string. The default + /// is "unknown/0.0". + /// + public string ProtocolVersion { get; set; } = Unknown; + + /// + /// The that the peer is connected on. + /// + /// + /// null when the peer is not connected to. + /// + public MultiAddress ConnectedAddress { get; set; } + + /// + /// The round-trip time it takes to get data from the peer. + /// + public TimeSpan? Latency { get; set; } + + /// + /// Determines if the information on the peer is valid. + /// + /// + /// true if all validation rules pass; otherwise false. + /// + /// + /// Verifies that + /// + /// The is defined + /// The is a hash of the + /// + /// + public bool IsValid() + { + if (Id == null) + return false; + if (PublicKey != null && !Id.Matches(Convert.FromBase64String(PublicKey))) + return false; + + return true; + } + + /// + public override int GetHashCode() { return ToString().GetHashCode(); } + + /// + public override bool Equals(object obj) + { + var that = obj as Peer; + return that == null + ? false + : Equals(that); + } + + /// + public bool Equals(Peer that) { return Id == that.Id; } + + /// + /// Value equality. + /// + public static bool operator ==(Peer a, Peer b) + { + if (ReferenceEquals(a, b)) return true; + if (a is null) return false; + if (b is null) return false; + + return a.Equals(b); + } + + /// + /// Value inequality. + /// + public static bool operator !=(Peer a, Peer b) { return !(a == b); } + + /// + /// Returns the encoding of the . + /// + /// + /// A Base58 representaton of the peer. + /// + public override string ToString() { return Id == null ? string.Empty : Id.ToBase58(); } + + /// + /// Implicit casting of a to a . + /// + /// + /// A encoded . + /// + /// + /// A new . + /// + /// + /// Equivalent to new Peer { Id = s } + /// + static public implicit operator Peer(string s) { return new Peer {Id = s}; } + } +} diff --git a/src/Lib.P2P/PeerConnection.cs b/src/Lib.P2P/PeerConnection.cs new file mode 100644 index 0000000000..678bf11ed5 --- /dev/null +++ b/src/Lib.P2P/PeerConnection.cs @@ -0,0 +1,437 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using Lib.P2P.Cryptography; +using Lib.P2P.Multiplex; +using Lib.P2P.Protocols; +using MultiFormats; + +namespace Lib.P2P +{ + /// + /// A connection between two peers. + /// + /// + /// A connection is used to exchange messages between peers. + /// + public class PeerConnection : IDisposable + { + private static ILog _log = LogManager.GetLogger(typeof(PeerConnection)); + + private Stream _stream; + private StatsStream _statsStream; + + /// + /// The local peer. + /// + public Peer LocalPeer { get; set; } + + /// + /// The remote peer. + /// + public Peer RemotePeer { get; set; } + + /// + /// The local peer's end point. + /// + public MultiAddress LocalAddress { get; set; } + + /// + /// The remote peer's end point. + /// + public MultiAddress RemoteAddress { get; set; } + + /// + /// The private key of the local peer. + /// + /// + /// Used to prove the identity of the . + /// + public Key LocalPeerKey { get; set; } + + /// + /// Determine which peer (local or remote) initiated the connection. + /// + /// + /// true if the initiated the connection; + /// otherwise, false. + /// + public bool IsIncoming { get; set; } + + /// + /// Determines if the connection to the remote can be used. + /// + /// + /// true if the connection is active. + /// + public bool IsActive => Stream != null && Stream.CanRead && Stream.CanWrite; + + /// + /// The duplex stream between the two peers. + /// + public Stream Stream + { + get => _stream; + set + { + if (value != null && _statsStream == null) + { + _statsStream = new StatsStream(value); + value = _statsStream; + } + + _stream = value; + } + } + + /// + /// The protocols that the connection will handle. + /// + /// + /// The key is a protocol name, such as "/mplex/6.7.0". The value + /// is a function that will process the protocol message. + /// + /// + /// + public Dictionary> Protocols { get; } + = new Dictionary>(); + + /// + /// Add a protocol that the connection will handle. + /// + /// + /// A peer protocol to add. + /// + public void AddProtocol(IPeerProtocol protocol) + { + Protocols.Add(protocol.ToString(), protocol.ProcessMessageAsync); + } + + /// + /// Add a seequence of protocols that the connection will handle. + /// + /// + /// The peer protocols to add. + /// + public void AddProtocols(IEnumerable protocols) + { + foreach (var protocol in protocols) + if (protocol != null) + Protocols.Add(protocol.ToString(), protocol.ProcessMessageAsync); + } + + /// + /// Signals that the security for the connection is established. + /// + /// + /// This can be awaited. + /// + public TaskCompletionSource SecurityEstablished { get; } = new TaskCompletionSource(); + + /// + /// Signals that the muxer for the connection is established. + /// + /// + /// This can be awaited. + /// + public TaskCompletionSource MuxerEstablished { get; } = new TaskCompletionSource(); + + /// + /// Signals that the identity of the remote endpoint is established. + /// + /// + /// This can be awaited. + /// + /// + /// The data in is not complete until + /// the identity is establish. + /// + public TaskCompletionSource IdentityEstablished { get; } = new TaskCompletionSource(); + + /// + /// When the connection was last used. + /// + public DateTime LastUsed => _statsStream.LastUsed; + + /// + /// Number of bytes read over the connection. + /// + public long BytesRead => _statsStream.BytesRead; + + /// + /// Number of bytes written over the connection. + /// + public long BytesWritten => _statsStream.BytesWritten; + + /// + /// Establish the connection with the remote node. + /// + /// + /// + /// + /// This should be called when the local peer wants a connection with + /// the remote peer. + /// + public async Task InitiateAsync(IEnumerable securityProtocols, + CancellationToken cancel = default) + { + await EstablishProtocolAsync("/multistream/", cancel).ConfigureAwait(false); + + // Find the first security protocol that is also supported by the remote. + List exceptions = new(); + foreach (var protocol in securityProtocols) + { + try + { + await EstablishProtocolAsync(protocol.ToString(), cancel).ConfigureAwait(false); + } + catch (Exception e) + { + exceptions.Add(e); + continue; + } + + await protocol.EncryptAsync(this, cancel).ConfigureAwait(false); + break; + } + + if (!SecurityEstablished.Task.IsCompleted) + { + throw new AggregateException("Could not establish a secure connection.", exceptions); + } + + await EstablishProtocolAsync("/multistream/", cancel).ConfigureAwait(false); + await EstablishProtocolAsync("/mplex/", cancel).ConfigureAwait(false); + + var muxer = new Muxer + { + Channel = Stream, + Initiator = true, + Connection = this + }; + muxer.SubstreamCreated += (s, e) => _ = ReadMessagesAsync(e, CancellationToken.None); + MuxerEstablished.SetResult(muxer); + + _ = muxer.ProcessRequestsAsync(cancel); + } + + /// + /// TODO: + /// + /// + /// + /// + internal Task EstablishProtocolAsync(string name, CancellationToken cancel) + { + return EstablishProtocolAsync(name, Stream, cancel); + } + + /// + /// TODO: + /// + /// + /// + /// + /// + public async Task EstablishProtocolAsync(string name, + Stream stream, + CancellationToken cancel = default(CancellationToken)) + { + var protocols = ProtocolRegistry.Protocols.Keys + .Where(k => k == name || k.StartsWith(name)) + .Select(k => VersionedName.Parse(k)) + .OrderByDescending(vn => vn) + .Select(vn => vn.ToString()); + foreach (var protocol in protocols) + { + await Message.WriteAsync(protocol, stream, cancel).ConfigureAwait(false); + var result = await Message.ReadStringAsync(stream, cancel).ConfigureAwait(false); + if (result == protocol) return; + } + + if (protocols.Count() == 0) throw new Exception($"Protocol '{name}' is not registered."); + throw new Exception($"{RemotePeer.Id} does not support protocol '{name}'."); + } + + /// + /// Starts reading messages from the remote peer. + /// + internal async Task ReadMessagesAsync(CancellationToken cancel) + { + _log.Debug($"start reading messsages from {RemoteAddress}"); + + // TODO: Only a subset of protocols are allowed until + // the remote is authenticated. + IPeerProtocol protocol = new Multistream1(); + try + { + while (!cancel.IsCancellationRequested && Stream != null) + { + await protocol.ProcessMessageAsync(this, Stream, cancel).ConfigureAwait(false); + } + } + catch (IOException e) + { + _log.Error("reading message failed " + e.Message); + + // eat it. + } + catch (Exception e) + { + if (!cancel.IsCancellationRequested && Stream != null) + { + _log.Error("reading message failed", e); + } + } + + // Ignore any disposal exceptions. + try + { +#if NETCOREAPP3_0 || NETCOREAPP3_1 + await Stream.DisposeAsync().ConfigureAwait(false); +#else + Stream.Dispose(); +#endif + } + catch (Exception) + { + // eat it. + } + + _log.Debug($"stop reading messsages from {RemoteAddress}"); + } + + /// + /// Starts reading messages from the remote peer on the specified stream. + /// + internal async Task ReadMessagesAsync(Stream stream, CancellationToken cancel) + { + IPeerProtocol protocol = new Multistream1(); + try + { + while (!cancel.IsCancellationRequested && stream != null && stream.CanRead) + { + await protocol.ProcessMessageAsync(this, stream, cancel).ConfigureAwait(false); + } + } + catch (EndOfStreamException) + { + // eat it. + } + catch (Exception e) + { + if (!cancel.IsCancellationRequested && stream != null) + { + _log.Error($"reading message failed {RemoteAddress} {RemotePeer}", e); + } + } + } + + #region IDisposable Support + + private bool _disposedValue; // To detect redundant calls + + /// + /// Signals that the connection is closed (disposed). + /// + public event EventHandler Closed; + + /// + /// TODO + /// + /// + protected virtual void Dispose(bool disposing) + { + if (_disposedValue) + { + return; + } + + _disposedValue = true; + + if (!disposing) + { + return; + } + + _log.Debug($"Closing connection to {RemoteAddress}"); + if (Stream != null) + { + try + { + Stream.Dispose(); + } + catch (ObjectDisposedException) + { + // ignore stream already closed. + } + catch (Exception e) + { + _log.Warn($"Failed to close connection to {RemoteAddress}", e); + + // eat it. + } + finally + { + Stream = null; + _statsStream = null; + } + } + + SecurityEstablished.TrySetCanceled(); + IdentityEstablished.TrySetCanceled(); + IdentityEstablished.TrySetCanceled(); + Closed?.Invoke(this, this); + + // free unmanaged resources (unmanaged objects) and override a finalizer below. + // set large fields to null. + } + + // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. + // ~PeerConnection() { + // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + // Dispose(false); + // } + + /// + /// + /// + public void Dispose() + { + // Do not change this code. Put cleanup code in Dispose(bool disposing) above. + Dispose(true); + + // TODO: uncomment the following line if the finalizer is overridden above. + // GC.SuppressFinalize(this); + } + + #endregion + } +} diff --git a/src/Lib.P2P/PeerManager.cs b/src/Lib.P2P/PeerManager.cs new file mode 100644 index 0000000000..3fdd40b2ec --- /dev/null +++ b/src/Lib.P2P/PeerManager.cs @@ -0,0 +1,211 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; + +namespace Lib.P2P +{ + /// + /// Manages the peers. + /// + /// + /// Listens to the events to determine the state + /// of a peer. + /// + public class PeerManager : IService + { + private static ILog _log = LogManager.GetLogger(typeof(PeerManager)); + private CancellationTokenSource _cancel; + + /// + /// Initial time to wait before attempting a reconnection + /// to a dead peer. + /// + /// + /// Defaults to 1 minute. + /// + public TimeSpan InitialBackoff = TimeSpan.FromMinutes(1); + + /// + /// When reached, the peer is considered permanently dead. + /// + /// + /// Defaults to 64 minutes. + /// + public TimeSpan MaxBackoff = TimeSpan.FromMinutes(64); + + /// + /// Provides access to other peers. + /// + public ISwarmService SwarmService { get; set; } + + /// + /// The peers that are reachable. + /// + public ConcurrentDictionary DeadPeers = new(); + + /// + public Task StartAsync() + { + SwarmService.ConnectionEstablished += Swarm_ConnectionEstablished; + SwarmService.PeerNotReachable += Swarm_PeerNotReachable; + + _cancel = new CancellationTokenSource(); + var _ = PhoenixAsync(_cancel.Token); + + _log.Debug("started"); + return Task.CompletedTask; + } + + /// + public Task StopAsync() + { + SwarmService.ConnectionEstablished -= Swarm_ConnectionEstablished; + SwarmService.PeerNotReachable -= Swarm_PeerNotReachable; + DeadPeers.Clear(); + + _cancel.Cancel(); + _cancel.Dispose(); + + _log.Debug("stopped"); + return Task.CompletedTask; + } + + /// + /// Indicates that the peer can not be connected to. + /// + /// + public void SetNotReachable(Peer peer) + { + var dead = DeadPeers.AddOrUpdate(peer, + new DeadPeer + { + Peer = peer, + Backoff = InitialBackoff, + NextAttempt = DateTime.Now + InitialBackoff + }, + (key, existing) => + { + existing.Backoff += existing.Backoff; + existing.NextAttempt = existing.Backoff <= MaxBackoff + ? DateTime.Now + existing.Backoff + : DateTime.MaxValue; + return existing; + }); + + SwarmService.BlackList.Add($"/p2p/{peer.Id}"); + if (dead.NextAttempt != DateTime.MaxValue) + { + _log.DebugFormat("Dead '{0}' for {1} minutes.", dead.Peer, dead.Backoff.TotalMinutes); + } + else + { + SwarmService.DeregisterPeer(dead.Peer); + _log.DebugFormat("Permanently dead '{0}'.", dead.Peer); + } + } + + /// + /// Indicates that the peer can be connected to. + /// + /// + public void SetReachable(Peer peer) + { + _log.DebugFormat("Alive '{0}'.", peer); + + DeadPeers.TryRemove(peer, out _); + SwarmService.BlackList.Remove($"/p2p/{peer.Id}"); + } + + /// + /// Is invoked by the when a peer can not be connected to. + /// + private void Swarm_PeerNotReachable(object sender, Peer peer) { SetNotReachable(peer); } + + /// + /// Is invoked by the when a peer is connected to. + /// + private void Swarm_ConnectionEstablished(object sender, PeerConnection connection) + { + SetReachable(connection.RemotePeer); + } + + /// + /// Background process to try reconnecting to a dead peer. + /// + private async Task PhoenixAsync(CancellationToken cancellation) + { + while (!cancellation.IsCancellationRequested) + try + { + await Task.Delay(InitialBackoff, cancellation); + var now = DateTime.Now; + await DeadPeers.Values + .Where(p => p.NextAttempt < now) + .ParallelForEachAsync(async dead => + { + _log.DebugFormat("Attempt reconnect to {0}", dead.Peer); + SwarmService.BlackList.Remove($"/p2p/{dead.Peer.Id}"); + try + { + await SwarmService.ConnectAsync(dead.Peer, _cancel.Token); + } + catch + { + // eat it + } + }, 10); + } + catch + { + // eat it. + } + } + } + + /// + /// Information on a peer that is not reachable. + /// + public class DeadPeer + { + /// + /// The peer that does not respond. + /// + public Peer Peer { get; set; } + + /// + /// How long to wait before attempting another connect. + /// + public TimeSpan Backoff { get; set; } + + /// + /// When another connect should be tried. + /// + public DateTime NextAttempt { get; set; } + } +} diff --git a/src/Lib.P2P/PeerTalk.sln b/src/Lib.P2P/PeerTalk.sln new file mode 100644 index 0000000000..2b2864159c --- /dev/null +++ b/src/Lib.P2P/PeerTalk.sln @@ -0,0 +1,45 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2027 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6B9AA9C7-5E90-4D10-9DDE-A2AA5FE3ECA6}" + ProjectSection(SolutionItems) = preProject + .gitignore = .gitignore + .travis.yml = .travis.yml + appveyor.yml = appveyor.yml + .circleci\config.yml = .circleci\config.yml + LICENSE = LICENSE + README.md = README.md + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PeerTalk", "src\PeerTalk.csproj", "{AA6F77C1-13CB-4B20-AB5E-3C6D237A562B}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "PeerTalkTests", "test\PeerTalkTests.csproj", "{B0977D09-CBA2-4D4B-BE66-08CE6D63DE1E}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Documentation", "doc\Documentation.csproj", "{D1B4DC93-5655-4492-8478-7BF1C90D7A9A}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {AA6F77C1-13CB-4B20-AB5E-3C6D237A562B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA6F77C1-13CB-4B20-AB5E-3C6D237A562B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA6F77C1-13CB-4B20-AB5E-3C6D237A562B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA6F77C1-13CB-4B20-AB5E-3C6D237A562B}.Release|Any CPU.Build.0 = Release|Any CPU + {B0977D09-CBA2-4D4B-BE66-08CE6D63DE1E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B0977D09-CBA2-4D4B-BE66-08CE6D63DE1E}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B0977D09-CBA2-4D4B-BE66-08CE6D63DE1E}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B0977D09-CBA2-4D4B-BE66-08CE6D63DE1E}.Release|Any CPU.Build.0 = Release|Any CPU + {D1B4DC93-5655-4492-8478-7BF1C90D7A9A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D1B4DC93-5655-4492-8478-7BF1C90D7A9A}.Release|Any CPU.ActiveCfg = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {36ED5AA7-8F41-4F7D-A665-230635EF64A1} + EndGlobalSection +EndGlobal diff --git a/src/Lib.P2P/Policy.cs b/src/Lib.P2P/Policy.cs new file mode 100644 index 0000000000..d4796c62bf --- /dev/null +++ b/src/Lib.P2P/Policy.cs @@ -0,0 +1,64 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Lib.P2P +{ + /// + /// A base for defining a policy. + /// + /// + /// The type of object that the rule applies to. + /// + public abstract class Policy : IPolicy + { + /// + public abstract bool IsAllowed(T target); + + /// + public bool IsNotAllowed(T target) { return !IsAllowed(target); } + } + + /// + /// A rule that always passes. + /// + /// + /// The type of object that the rule applies to. + /// + public class PolicyAlways : Policy + { + /// + public override bool IsAllowed(T target) { return true; } + } + + /// + /// A rule that always fails. + /// + /// + /// The type of object that the rule applies to. + /// + public class PolicyNever : Policy + { + /// + public override bool IsAllowed(T target) { return false; } + } +} diff --git a/src/Lib.P2P/ProtoBufHelper.cs b/src/Lib.P2P/ProtoBufHelper.cs new file mode 100644 index 0000000000..df192f6811 --- /dev/null +++ b/src/Lib.P2P/ProtoBufHelper.cs @@ -0,0 +1,63 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MultiFormats; + +namespace Lib.P2P +{ + /// + /// Helper methods for ProtoBuf. + /// + public static class ProtoBufHelper + { + /// + /// Read a proto buf message with a varint length prefix. + /// + /// + /// The type of message. + /// + /// + /// The stream containing the message. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the message. + /// + public static async Task ReadMessageAsync(Stream stream, + CancellationToken cancel = default) + { + var length = await stream.ReadVarint32Async(cancel).ConfigureAwait(false); + var bytes = new byte[length]; + await stream.ReadExactAsync(bytes, 0, length, cancel).ConfigureAwait(false); + + await using MemoryStream ms = new(bytes, false); + return ProtoBuf.Serializer.Deserialize(ms); + } + } +} diff --git a/src/Lib.P2P/Protocols/IEncryptionProtocol.cs b/src/Lib.P2P/Protocols/IEncryptionProtocol.cs new file mode 100644 index 0000000000..2f83dde6a6 --- /dev/null +++ b/src/Lib.P2P/Protocols/IEncryptionProtocol.cs @@ -0,0 +1,50 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Lib.P2P.Protocols +{ + /// + /// Applies encryption to a . + /// + public interface IEncryptionProtocol : IPeerProtocol + { + /// + /// Creates an encrypted stream for the connection. + /// + /// + /// A connection between two peers. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the encrypted stream. + /// + Task EncryptAsync(PeerConnection connection, CancellationToken cancel = default); + } +} diff --git a/src/Lib.P2P/Protocols/IPeerProtocol.cs b/src/Lib.P2P/Protocols/IPeerProtocol.cs new file mode 100644 index 0000000000..a9e8b999a0 --- /dev/null +++ b/src/Lib.P2P/Protocols/IPeerProtocol.cs @@ -0,0 +1,70 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Semver; + +namespace Lib.P2P.Protocols +{ + /// + /// Defines the messages that can be exchanged between two peers. + /// + /// + /// must return a string in the form + /// "/name/version". + /// + public interface IPeerProtocol + { + /// + /// The name of the protocol. + /// + string Name { get; } + + /// + /// The version of the protocol. + /// + SemVersion Version { get; } + + /// + /// Process a message for the protocol. + /// + /// + /// A connection between two peers. + /// + /// + /// The message source. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task ProcessMessageAsync(PeerConnection connection, + Stream stream, + CancellationToken cancel = default); + } +} diff --git a/src/Lib.P2P/Protocols/Identify1.cs b/src/Lib.P2P/Protocols/Identify1.cs new file mode 100644 index 0000000000..2db4af49c0 --- /dev/null +++ b/src/Lib.P2P/Protocols/Identify1.cs @@ -0,0 +1,188 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using MultiFormats; +using ProtoBuf; +using Semver; + +namespace Lib.P2P.Protocols +{ + /// + /// Identifies the peer. + /// + public sealed class Identify1 : IPeerProtocol + { + private static ILog _log = LogManager.GetLogger(typeof(Identify1)); + + /// + public string Name { get; } = "ipfs/id"; + + /// + public SemVersion Version { get; } = new(1); + + /// + public override string ToString() { return $"/{Name}/{Version}"; } + + /// + public async Task ProcessMessageAsync(PeerConnection connection, + Stream stream, + CancellationToken cancel = default) + { + // Send our identity. + _log.Debug("Sending identity to " + connection.RemoteAddress); + var peer = connection.LocalPeer; + var res = new Identify + { + ProtocolVersion = peer.ProtocolVersion, + AgentVersion = peer.AgentVersion, + ListenAddresses = peer.Addresses + .Select(a => a.WithoutPeerId().ToArray()) + .ToArray(), + ObservedAddress = connection.RemoteAddress?.ToArray(), + Protocols = null, // no longer sent + }; + + if (peer.PublicKey != null) + { + res.PublicKey = Convert.FromBase64String(peer.PublicKey); + } + + Serializer.SerializeWithLengthPrefix(stream, res, PrefixStyle.Base128); + await stream.FlushAsync(cancel).ConfigureAwait(false); + } + + /// + /// Gets the identity information of the remote peer. + /// + /// + /// The currenty connection to the remote peer. + /// + /// + /// + public async Task GetRemotePeerAsync(PeerConnection connection, CancellationToken cancel) + { + var muxer = await connection.MuxerEstablished.Task.ConfigureAwait(false); + _log.Debug("Get remote identity"); + var remote = connection.RemotePeer; + if (remote == null) + { + remote = new Peer(); + connection.RemotePeer = remote; + } + + // Read the remote peer identify info. + await using (var stream = await muxer.CreateStreamAsync("id", cancel).ConfigureAwait(false)) + { + await connection.EstablishProtocolAsync("/multistream/", stream, cancel).ConfigureAwait(false); + await connection.EstablishProtocolAsync("/ipfs/id/", stream, cancel).ConfigureAwait(false); + await UpdateRemotePeerAsync(remote, stream, cancel).ConfigureAwait(false); + } + + // It should always contain the address we used for connections, so + // that NAT translations are maintained. + if (connection.RemoteAddress != null && !remote.Addresses.Contains(connection.RemoteAddress)) + { + var addrs = remote.Addresses.ToList(); + addrs.Add(connection.RemoteAddress); + remote.Addresses = addrs; + } + + connection.IdentityEstablished.TrySetResult(remote); + + _log.Debug($"Peer id '{remote}' of {connection.RemoteAddress}"); + return remote; + } + + /// + /// Read the identify message and update the peer information. + /// + /// + /// + /// + /// + public async Task UpdateRemotePeerAsync(Peer remote, Stream stream, CancellationToken cancel) + { + var info = await ProtoBufHelper.ReadMessageAsync(stream, cancel).ConfigureAwait(false); + + remote.AgentVersion = info.AgentVersion; + remote.ProtocolVersion = info.ProtocolVersion; + if (info.PublicKey == null || info.PublicKey.Length == 0) + { + throw new InvalidDataException("Public key is missing."); + } + + remote.PublicKey = Convert.ToBase64String(info.PublicKey); + if (remote.Id == null) + { + remote.Id = MultiHash.ComputeHash(info.PublicKey); + } + + if (info.ListenAddresses != null) + { + remote.Addresses = info.ListenAddresses + .Select(MultiAddress.TryCreate) + .Where(a => a != null) + .Select(a => a.WithPeerId(remote.Id)) + .ToList(); + } + + if (!remote.Addresses.Any()) + { + _log.Warn($"No listen address for {remote}"); + } + + if (!remote.IsValid()) + { + throw new InvalidDataException($"Invalid peer {remote}."); + } + } + + [ProtoContract] + private sealed class Identify + { + [ProtoMember(5)] + public string ProtocolVersion; + + [ProtoMember(6)] + public string AgentVersion; + + [ProtoMember(1)] + public byte[] PublicKey; + + [ProtoMember(2, IsRequired = true)] + public byte[][] ListenAddresses; + + [ProtoMember(4)] + public byte[] ObservedAddress; + + [ProtoMember(3)] + public string[] Protocols; + } + } +} diff --git a/src/Lib.P2P/Protocols/Message.cs b/src/Lib.P2P/Protocols/Message.cs new file mode 100644 index 0000000000..e649f28799 --- /dev/null +++ b/src/Lib.P2P/Protocols/Message.cs @@ -0,0 +1,139 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using MultiFormats; + +namespace Lib.P2P.Protocols +{ + /// + /// A message that is exchanged between peers. + /// + /// + /// A message consists of + /// + /// A length prefix + /// The payload + /// A terminating newline + /// + /// + public static class Message + { + private static byte[] _newline = new byte[] {0x0a}; + private static ILog _log = LogManager.GetLogger(typeof(Message)); + + /// + /// Read the message as a sequence of bytes from the . + /// + /// + /// The to a peer. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the byte representation of the message's payload. + /// + /// + /// When the message is invalid. + /// + public static async Task ReadBytesAsync(Stream stream, + CancellationToken cancel = default) + { + var eol = new byte[1]; + var length = await stream.ReadVarint32Async(cancel).ConfigureAwait(false); + var buffer = new byte[length - 1]; + await stream.ReadExactAsync(buffer, 0, length - 1, cancel).ConfigureAwait(false); + await stream.ReadExactAsync(eol, 0, 1, cancel).ConfigureAwait(false); + if (eol[0] != _newline[0]) + { + _log.Error($"length: {length}, bytes: {buffer.ToHexString()}"); + throw new InvalidDataException("Missing terminating newline"); + } + + return buffer; + } + + /// + /// Read the message as a from the . + /// + /// + /// The to a peer. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the string representation of the message's payload. + /// + /// + /// When the message is invalid. + /// + /// + /// The return value has the length prefix and terminating newline removed. + /// + public static async Task ReadStringAsync(Stream stream, + CancellationToken cancel = default) + { + var bytes = await ReadBytesAsync(stream, cancel).ConfigureAwait(false); + var payload = Encoding.UTF8.GetString(bytes); + + _log.Trace("received " + payload); + return payload; + } + + /// + /// Writes the binary representation of the message to the specified . + /// + /// + /// The message to write. A newline is automatically appended. + /// + /// + /// The to a peer. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + public static async Task WriteAsync(string message, + Stream stream, + CancellationToken cancel = default) + { + _log.Trace("sending " + message); + + var payload = Encoding.UTF8.GetBytes(message); + await stream.WriteVarintAsync(message.Length + 1, cancel).ConfigureAwait(false); + await stream.WriteAsync(payload, 0, payload.Length, cancel).ConfigureAwait(false); + await stream.WriteAsync(_newline, 0, _newline.Length, cancel).ConfigureAwait(false); + await stream.FlushAsync(cancel).ConfigureAwait(false); + } + } +} diff --git a/src/Lib.P2P/Protocols/Mplex67.cs b/src/Lib.P2P/Protocols/Mplex67.cs new file mode 100644 index 0000000000..8a8b1a3014 --- /dev/null +++ b/src/Lib.P2P/Protocols/Mplex67.cs @@ -0,0 +1,78 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using Lib.P2P.Multiplex; +using Semver; + +namespace Lib.P2P.Protocols +{ + /// + /// A Stream Multiplexer protocol. + /// + /// + public class Mplex67 : IPeerProtocol + { + private static ILog _log = LogManager.GetLogger(typeof(Mplex67)); + + /// + public string Name { get; } = "mplex"; + + /// + public SemVersion Version { get; } = new(6, 7); + + /// + public override string ToString() { return $"/{Name}/{Version}"; } + + /// + public async Task ProcessMessageAsync(PeerConnection connection, + Stream stream, + CancellationToken cancel = default) + { + _log.Debug("start processing requests from " + connection.RemoteAddress); + var muxer = new Muxer + { + Channel = stream, + Connection = connection, + Receiver = true + }; + muxer.SubstreamCreated += (s, e) => _ = connection.ReadMessagesAsync(e, CancellationToken.None); + + // Attach muxer to the connection. It now becomes the message reader. + connection.MuxerEstablished.SetResult(muxer); + await muxer.ProcessRequestsAsync(cancel).ConfigureAwait(false); + + _log.Debug("stop processing from " + connection.RemoteAddress); + } + + /// + public Task ProcessResponseAsync(PeerConnection connection, + CancellationToken cancel = default) + { + return Task.CompletedTask; + } + } +} diff --git a/src/Lib.P2P/Protocols/Multistream1.cs b/src/Lib.P2P/Protocols/Multistream1.cs new file mode 100644 index 0000000000..05857db2d6 --- /dev/null +++ b/src/Lib.P2P/Protocols/Multistream1.cs @@ -0,0 +1,75 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using Semver; + +namespace Lib.P2P.Protocols +{ + /// + /// A protocol to select other protocols. + /// + /// + public sealed class Multistream1 : IPeerProtocol + { + private static ILog _log = LogManager.GetLogger(typeof(Multistream1)); + + /// + public string Name { get; } = "multistream"; + + /// + public SemVersion Version { get; } = new(1); + + /// + public override string ToString() { return $"/{Name}/{Version}"; } + + /// + public async Task ProcessMessageAsync(PeerConnection connection, + Stream stream, + CancellationToken cancel = default) + { + var msg = await Message.ReadStringAsync(stream, cancel).ConfigureAwait(false); + + // TODO: msg == "ls" + if (msg == "ls") throw new NotImplementedException("multistream ls"); + + // Switch to the specified protocol + if (!connection.Protocols.TryGetValue(msg, out var protocol)) + { + await Message.WriteAsync("na", stream, cancel).ConfigureAwait(false); + return; + } + + // Ack protocol switch + _log.Debug("switching to " + msg); + await Message.WriteAsync(msg, stream, cancel).ConfigureAwait(false); + + // Process protocol message. + await protocol(connection, stream, cancel).ConfigureAwait(false); + } + } +} diff --git a/src/Lib.P2P/Protocols/Ping1.cs b/src/Lib.P2P/Protocols/Ping1.cs new file mode 100644 index 0000000000..e7f3189969 --- /dev/null +++ b/src/Lib.P2P/Protocols/Ping1.cs @@ -0,0 +1,219 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using MultiFormats; +using Semver; + +namespace Lib.P2P.Protocols +{ + /// + /// Ping Protocol version 1.0 + /// + public class Ping1 : IPeerProtocol, IService + { + private const int PingSize = 32; + + private static ILog _log = LogManager.GetLogger(typeof(Ping1)); + + /// + public string Name { get; } = "ipfs/ping"; + + /// + public SemVersion Version { get; } = new(1); + + /// + /// Provides access to other peers. + /// + public ISwarmService SwarmService { get; set; } + + /// + public override string ToString() { return $"/{Name}/{Version}"; } + + /// + /// + /// + /// + public Ping1(ISwarmService swarmService) { SwarmService = swarmService; } + + /// + public async Task ProcessMessageAsync(PeerConnection connection, + Stream stream, + CancellationToken cancel = default) + { + while (true) + { + // Read the message. + var request = new byte[PingSize]; + await stream.ReadExactAsync(request, 0, PingSize, cancel).ConfigureAwait(false); + _log.Debug($"got ping from {connection.RemotePeer}"); + + // Echo the message + await stream.WriteAsync(request, 0, PingSize, cancel).ConfigureAwait(false); + await stream.FlushAsync(cancel).ConfigureAwait(false); + } + } + + /// + public Task StartAsync() + { + _log.Debug("Starting"); + + SwarmService.AddProtocol(this); + + return Task.CompletedTask; + } + + /// + public Task StopAsync() + { + _log.Debug("Stopping"); + + SwarmService.RemoveProtocol(this); + + return Task.CompletedTask; + } + + /// + /// Send echo requests to a peer. + /// + /// + /// The peer ID to receive the echo requests. + /// + /// + /// The number of echo requests to send. Defaults to 10. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the sequence of . + /// + public async Task> PingAsync(MultiHash peerId, + int count = 10, + CancellationToken cancel = default) + { + var peer = new Peer {Id = peerId}; + return await PingAsync(peer, count, cancel).ConfigureAwait(false); + } + + /// + /// Send echo requests to a peer. + /// + /// + /// The address of a peer to receive the echo requests. + /// + /// + /// The number of echo requests to send. Defaults to 10. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// the sequence of . + /// + public async Task> PingAsync(MultiAddress address, + int count = 10, + CancellationToken cancel = default) + { + var peer = SwarmService.RegisterPeerAddress(address); + return await PingAsync(peer, count, cancel).ConfigureAwait(false); + } + + private async Task> PingAsync(Peer peer, int count, CancellationToken cancel) + { + var ping = new byte[PingSize]; + Random rng = new(); + var results = new List + { + new PingResult {Success = true, Text = $"PING {peer}."} + }; + var totalTime = TimeSpan.Zero; + + await using (var stream = await SwarmService.DialAsync(peer, ToString(), cancel)) + { + for (var i = 0; i < count; ++i) + { + rng.NextBytes(ping); + + var start = DateTime.Now; + try + { + await stream.WriteAsync(ping, 0, ping.Length, cancel).ConfigureAwait(false); + + await stream.FlushAsync(cancel).ConfigureAwait(false); + + var response = new byte[PingSize]; + await stream.ReadExactAsync(response, 0, PingSize, cancel).ConfigureAwait(false); + + var result = new PingResult + { + Time = DateTime.Now - start, + }; + totalTime += result.Time; + if (ping.SequenceEqual(response)) + { + result.Success = true; + result.Text = ""; + } + else + { + result.Success = false; + result.Text = "ping packet was incorrect!"; + } + + results.Add(result); + } + catch (Exception e) + { + results.Add(new PingResult + { + Success = false, + Time = DateTime.Now - start, + Text = e.Message + }); + } + } + } + + var avg = totalTime.TotalMilliseconds / count; + results.Add(new PingResult + { + Success = true, + Text = $"Average latency: {avg.ToString("0.000")}ms" + }); + + return results; + } + } + + internal class PingMessage { } +} diff --git a/src/Lib.P2P/Protocols/PingResult.cs b/src/Lib.P2P/Protocols/PingResult.cs new file mode 100644 index 0000000000..ef44d10f79 --- /dev/null +++ b/src/Lib.P2P/Protocols/PingResult.cs @@ -0,0 +1,51 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; + +namespace Lib.P2P.Protocols +{ + /// + /// The result from sending a + /// Catalyst.Ipfs.Core.CoreApi.IGenericApi.PingAsync(MultiFormats.MultiHash,int,System.Threading.CancellationToken) + /// + /// . + /// + public class PingResult + { + /// + /// Indicates success or failure. + /// + public bool Success { get; set; } + + /// + /// The round trip time; nano second resolution. + /// + public TimeSpan Time { get; set; } + + /// + /// The text to echo. + /// + public string Text { get; set; } + } +} diff --git a/src/Lib.P2P/Protocols/Plaintext1.cs b/src/Lib.P2P/Protocols/Plaintext1.cs new file mode 100644 index 0000000000..48b18d486e --- /dev/null +++ b/src/Lib.P2P/Protocols/Plaintext1.cs @@ -0,0 +1,62 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Semver; + +namespace Lib.P2P.Protocols +{ + /// + /// TODO + /// + public class Plaintext1 : IEncryptionProtocol + { + /// + public string Name { get; } = "plaintext"; + + /// + public SemVersion Version { get; } = new(1); + + /// + public override string ToString() { return $"/{Name}/{Version}"; } + + /// + public async Task ProcessMessageAsync(PeerConnection connection, + Stream stream, + CancellationToken cancel = default) + { + connection.SecurityEstablished.SetResult(true); + await connection.EstablishProtocolAsync("/multistream/", CancellationToken.None).ConfigureAwait(false); + } + + /// + public Task EncryptAsync(PeerConnection connection, + CancellationToken cancel = default) + { + connection.SecurityEstablished.SetResult(true); + return Task.FromResult(connection.Stream); + } + } +} diff --git a/src/Lib.P2P/Protocols/ProtocolRegistry.cs b/src/Lib.P2P/Protocols/ProtocolRegistry.cs new file mode 100644 index 0000000000..19d7a498ec --- /dev/null +++ b/src/Lib.P2P/Protocols/ProtocolRegistry.cs @@ -0,0 +1,71 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; + +namespace Lib.P2P.Protocols +{ + /// + /// Metadata on . + /// + public static class ProtocolRegistry + { + /// + /// All the peer protocols. + /// + /// + /// The key is the name and version of the peer protocol, like "/multiselect/1.0.0". + /// The value is a Func that returns an new instance of the peer protocol. + /// + public static Dictionary> Protocols; + + static ProtocolRegistry() + { + Protocols = new Dictionary>(); + Register(); + Register(); + Register(); + Register(); + Register(); + } + + /// + /// Register a new protocol. + /// + /// + public static void Register() where T : IPeerProtocol, new() + { + T p = new(); + Protocols.Add(p.ToString(), () => new T()); + } + + /// + /// Remove the specified protocol. + /// + /// + /// The protocol name to remove. + /// + public static void Deregister(string protocolName) { Protocols.Remove(protocolName); } + } +} diff --git a/src/Lib.P2P/Protocols/VersionedName.cs b/src/Lib.P2P/Protocols/VersionedName.cs new file mode 100644 index 0000000000..938d3ad00a --- /dev/null +++ b/src/Lib.P2P/Protocols/VersionedName.cs @@ -0,0 +1,101 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using Semver; + +namespace Lib.P2P.Protocols +{ + /// + /// A name with a semantic version. + /// + /// + /// Implements value type equality. + /// + public sealed class VersionedName : IEquatable, IComparable + { + /// + /// The name. + /// + public string Name { get; set; } + + /// + /// The semantic version. + /// + public SemVersion Version { get; set; } + + /// + public override string ToString() { return $"/{Name}/{Version}"; } + + /// + /// Parse + /// + /// + /// + public static VersionedName Parse(string s) + { + var parts = s.Split('/').Where(p => p.Length > 0).ToArray(); + return new VersionedName + { + Name = string.Join("/", parts, 0, parts.Length - 1), + Version = SemVersion.Parse(parts[^1]) + }; + } + + /// + public override int GetHashCode() { return ToString().GetHashCode(); } + + /// + public override bool Equals(object obj) + { + var that = obj as VersionedName; + return that != null && (Name == that.Name && Version == that.Version); + } + + /// + public bool Equals(VersionedName that) { return Name == that?.Name && Version == that?.Version; } + + /// + /// Value equality. + /// + public static bool operator ==(VersionedName a, VersionedName b) + { + return ReferenceEquals(a, b) || !ReferenceEquals(a, null) && !ReferenceEquals(b, null) && a.Equals(b); + } + + /// + /// Value inequality. + /// + public static bool operator !=(VersionedName a, VersionedName b) + { + return !ReferenceEquals(a, b) && (ReferenceEquals(a, null) || ReferenceEquals(b, null) || !a.Equals(b)); + } + + /// + public int CompareTo(VersionedName that) + { + return that == null ? 1 : Name == that.Name ? Version.CompareTo(that.Version) : string.Compare(Name, that.Name, StringComparison.Ordinal); + } + } +} diff --git a/src/Lib.P2P/PubSub/FloodRouter.cs b/src/Lib.P2P/PubSub/FloodRouter.cs new file mode 100644 index 0000000000..61c10bce11 --- /dev/null +++ b/src/Lib.P2P/PubSub/FloodRouter.cs @@ -0,0 +1,321 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using Lib.P2P.Protocols; +using ProtoBuf; +using Semver; + +namespace Lib.P2P.PubSub +{ + /// + /// The original flood sub router. + /// + public class FloodRouter : IPeerProtocol, IMessageRouter + { + private static ILog _log = LogManager.GetLogger(typeof(FloodRouter)); + + private MessageTracker _tracker = new(); + private ConcurrentDictionary _localTopics = new(); + + /// + /// The topics of interest of other peers. + /// + public TopicManager RemoteTopics { get; set; } = new(); + + /// + public event EventHandler MessageReceived; + + /// + public string Name { get; } = "floodsub"; + + /// + public SemVersion Version { get; } = new(1); + + /// + public override string ToString() { return $"/{Name}/{Version}"; } + + /// + /// Provides access to other peers. + /// + public ISwarmService SwarmService { get; set; } + + public FloodRouter(ISwarmService swarmService) + { + SwarmService = swarmService; + } + + /// + public Task StartAsync() + { + _log.Debug("Starting"); + + SwarmService.AddProtocol(this); + SwarmService.ConnectionEstablished += Swarm_ConnectionEstablished; + SwarmService.PeerDisconnected += Swarm_PeerDisconnected; + + return Task.CompletedTask; + } + + /// + public Task StopAsync() + { + _log.Debug("Stopping"); + + SwarmService.ConnectionEstablished -= Swarm_ConnectionEstablished; + SwarmService.PeerDisconnected -= Swarm_PeerDisconnected; + SwarmService.RemoveProtocol(this); + RemoteTopics.Clear(); + _localTopics.Clear(); + + return Task.CompletedTask; + } + + /// + public async Task ProcessMessageAsync(PeerConnection connection, + Stream stream, + CancellationToken cancel = default) + { + while (true) + { + var request = await ProtoBufHelper.ReadMessageAsync(stream, cancel) + .ConfigureAwait(false); + + _log.Debug($"got message from {connection.RemotePeer}"); + + if (request.Subscriptions != null) + { + foreach (var sub in request.Subscriptions) + ProcessSubscription(sub, connection.RemotePeer); + } + + if (request.PublishedMessages == null) + { + continue; + } + + foreach (var msg in request.PublishedMessages) + { + _log.Debug($"Message for '{string.Join(", ", msg.Topics)}' fowarded by {connection.RemotePeer}"); + msg.Forwarder = connection.RemotePeer; + MessageReceived?.Invoke(this, msg); + await PublishAsync(msg, cancel).ConfigureAwait(false); + } + } + } + + /// + /// Process a subscription request from another peer. + /// + /// + /// The subscription request. + /// + /// + /// The remote . + /// + /// + /// + /// Maintains the . + /// + public void ProcessSubscription(Subscription sub, Peer remote) + { + if (sub.Subscribe) + { + _log.Debug($"Subscribe '{sub.Topic}' by {remote}"); + RemoteTopics.AddInterest(sub.Topic, remote); + } + else + { + _log.Debug($"Unsubscribe '{sub.Topic}' by {remote}"); + RemoteTopics.RemoveInterest(sub.Topic, remote); + } + } + + /// + public IEnumerable InterestedPeers(string topic) { return RemoteTopics.GetPeers(topic); } + + /// + public async Task JoinTopicAsync(string topic, CancellationToken cancel) + { + _localTopics.TryAdd(topic, topic); + var msg = new PubSubMessage + { + Subscriptions = new[] + { + new Subscription + { + Topic = topic, + Subscribe = true + } + } + }; + try + { + var peers = SwarmService.KnownPeers.Where(p => p.ConnectedAddress != null); + await SendAsync(msg, peers, cancel).ConfigureAwait(false); + } + catch (Exception e) + { + _log.Warn("Join topic failed.", e); + } + } + + /// + public async Task LeaveTopicAsync(string topic, CancellationToken cancel) + { + _localTopics.TryRemove(topic, out _); + var msg = new PubSubMessage + { + Subscriptions = new[] + { + new Subscription + { + Topic = topic, + Subscribe = false + } + } + }; + try + { + var peers = SwarmService.KnownPeers.Where(p => p.ConnectedAddress != null); + await SendAsync(msg, peers, cancel).ConfigureAwait(false); + } + catch (Exception e) + { + _log.Warn("Leave topic failed.", e); + } + } + + /// + public Task PublishAsync(PublishedMessage message, CancellationToken cancel) + { + if (_tracker.RecentlySeen(message.MessageId)) + { + return Task.CompletedTask; + } + + // Find a set of peers that are interested in the topic(s). + // Exclude author and sender + var peers = message.Topics + .SelectMany(topic => RemoteTopics.GetPeers(topic)) + .Where(peer => peer != message.Sender) + .Where(peer => peer != message.Forwarder); + + // Forward the message. + var forward = new PubSubMessage + { + PublishedMessages = new[] {message} + }; + + return SendAsync(forward, peers, cancel); + } + + private Task SendAsync(PubSubMessage msg, IEnumerable peers, CancellationToken cancel) + { + // Get binary representation + byte[] bin; + using (MemoryStream ms = new()) + { + Serializer.SerializeWithLengthPrefix(ms, msg, PrefixStyle.Base128); + bin = ms.ToArray(); + } + + return Task.WhenAll(peers.Select(p => SendAsync(bin, p, cancel))); + } + + private async Task SendAsync(byte[] message, Peer peer, CancellationToken cancel) + { + try + { + await using (var stream = await SwarmService.DialAsync(peer, ToString(), cancel).ConfigureAwait(false)) + { + await stream.WriteAsync(message, 0, message.Length, cancel).ConfigureAwait(false); + await stream.FlushAsync(cancel).ConfigureAwait(false); + } + + _log.Debug($"sending message to {peer}"); + } + catch (Exception e) + { + _log.Debug($"{peer} refused pubsub message.", e); + } + } + +#pragma warning disable VSTHRD100 // Avoid async void methods + /// + /// Raised when a connection is established to a remote peer. + /// + /// + /// + /// + /// Sends the hello message to the remote peer. The message contains + /// all topics that are of interest to the local peer. + /// + private async void Swarm_ConnectionEstablished(object sender, PeerConnection connection) +#pragma warning restore VSTHRD100 // Avoid async void methods + { + if (_localTopics.Count == 0) + { + return; + } + + try + { + var hello = new PubSubMessage + { + Subscriptions = _localTopics.Values + .Select(topic => new Subscription + { + Subscribe = true, + Topic = topic + }) + .ToArray() + }; + await SendAsync(hello, new[] {connection.RemotePeer}, CancellationToken.None) + .ConfigureAwait(false); + } + catch (Exception e) + { + _log.Warn("Sending hello message failed", e); + } + } + + /// + /// Raised when the peer has no more connections. + /// + /// + /// + /// + /// Removes the from the + /// . + /// + private void Swarm_PeerDisconnected(object sender, Peer peer) { RemoteTopics.Clear(peer); } + } +} diff --git a/src/Lib.P2P/PubSub/IMessageRouter.cs b/src/Lib.P2P/PubSub/IMessageRouter.cs new file mode 100644 index 0000000000..907bac8840 --- /dev/null +++ b/src/Lib.P2P/PubSub/IMessageRouter.cs @@ -0,0 +1,95 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Lib.P2P.PubSub +{ + /// + /// Routes pub/sub messages to other peers. + /// + public interface IMessageRouter : IService + { + /// + /// Sends the message to other peers. + /// + /// + /// The message to send. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task PublishAsync(PublishedMessage message, CancellationToken cancel); + + /// + /// Raised when a new message is received. + /// + event EventHandler MessageReceived; + + /// + /// Gets the sequence of peers interested in the topic. + /// + /// + /// The topic of interest or null for all topics. + /// + /// + /// A sequence of that are subsribed to the + /// . + /// + IEnumerable InterestedPeers(string topic); + + /// + /// Indicates that the local peer is interested in the topic. + /// + /// + /// The topic of interested. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task JoinTopicAsync(string topic, CancellationToken cancel); + + /// + /// Indicates that the local peer is no longer interested in the topic. + /// + /// + /// The topic of interested. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task LeaveTopicAsync(string topic, CancellationToken cancel); + } +} diff --git a/src/Lib.P2P/PubSub/IPubSub.cs b/src/Lib.P2P/PubSub/IPubSub.cs new file mode 100644 index 0000000000..2aec3d2369 --- /dev/null +++ b/src/Lib.P2P/PubSub/IPubSub.cs @@ -0,0 +1,136 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Lib.P2P.PubSub +{ + /// + /// + /// + public interface IPubSub + { + /// + /// Get the subscribed topics. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// a sequence of for each topic. + /// + Task> SubscribedTopicsAsync(CancellationToken cancel = default); + + /// + /// Get the peers that are pubsubing with us. + /// + /// + /// When specified, only peers subscribing on the topic are returned. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's value is + /// a sequence of . + /// + Task> PeersAsync(string topic = null, CancellationToken cancel = default); + + /// + /// Publish a string message to a given topic. + /// + /// + /// The topic name. + /// + /// + /// The message to publish. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task PublishAsync(string topic, string message, CancellationToken cancel = default); + + /// + /// Publish a binary message to a given topic. + /// + /// + /// The topic name. + /// + /// + /// The message to publish. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task PublishAsync(string topic, byte[] message, CancellationToken cancel = default); + + /// + /// Publish a binary message to a given topic. + /// + /// + /// The topic name. + /// + /// + /// The message to publish. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task PublishAsync(string topic, Stream message, CancellationToken cancel = default); + + /// + /// Subscribe to messages on a given topic. + /// + /// + /// The topic name. + /// + /// + /// The action to perform when a is received. + /// + /// + /// Is used to stop the topic listener. When cancelled, the + /// is NOT raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// The is invoked on the topic listener thread. + /// + Task SubscribeAsync(string topic, Action handler, CancellationToken cancellationToken); + } +} diff --git a/src/Lib.P2P/PubSub/IPubSubService.cs b/src/Lib.P2P/PubSub/IPubSubService.cs new file mode 100644 index 0000000000..c135f2f649 --- /dev/null +++ b/src/Lib.P2P/PubSub/IPubSubService.cs @@ -0,0 +1,61 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Lib.P2P.PubSub +{ + /// + /// + /// + public interface IPubSubService : IService, IPubSub + { + /// + /// The local peer. + /// + Peer LocalPeer { get; set; } + + /// + /// Sends and receives messages to other peers. + /// + List Routers { get; set; } + + /// + /// Creates a message for the topic and data. + /// + /// + /// The topic name/id. + /// + /// + /// The payload of message. + /// + /// + /// A unique published message. + /// + /// + /// The is a monitonically + /// increasing unsigned long. + /// + PublishedMessage CreateMessage(string topic, byte[] data); + } +} diff --git a/src/Lib.P2P/PubSub/IPublishedMessage.cs b/src/Lib.P2P/PubSub/IPublishedMessage.cs new file mode 100644 index 0000000000..cbff443aaf --- /dev/null +++ b/src/Lib.P2P/PubSub/IPublishedMessage.cs @@ -0,0 +1,60 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; + +namespace Lib.P2P.PubSub +{ + /// + /// A published message. + /// + /// + /// The is used to publish and subsribe to a message. + /// + public interface IPublishedMessage : IDataBlock + { + /// + /// The sender of the message. + /// + /// + /// The peer that sent the message. + /// + Peer Sender { get; } + + /// + /// The topics of the message. + /// + /// + /// All topics related to this message. + /// + IEnumerable Topics { get; } + + /// + /// The sequence number of the message. + /// + /// + /// A sender unique id for the message. + /// + byte[] SequenceNumber { get; } + } +} diff --git a/src/Lib.P2P/PubSub/LoopbackRouter.cs b/src/Lib.P2P/PubSub/LoopbackRouter.cs new file mode 100644 index 0000000000..ac554a8460 --- /dev/null +++ b/src/Lib.P2P/PubSub/LoopbackRouter.cs @@ -0,0 +1,72 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Lib.P2P.PubSub +{ + /// + /// A message router that always raises + /// when a message is published. + /// + /// + /// The allows the to invoke the + /// local subscribtion handlers. + /// + public class LoopbackRouter : IMessageRouter + { + private MessageTracker _tracker = new(); + + /// + public event EventHandler MessageReceived; + + /// + public IEnumerable InterestedPeers(string topic) { return Enumerable.Empty(); } + + /// + public Task JoinTopicAsync(string topic, CancellationToken cancel) { return Task.CompletedTask; } + + /// + public Task LeaveTopicAsync(string topic, CancellationToken cancel) { return Task.CompletedTask; } + + /// + public Task PublishAsync(PublishedMessage message, CancellationToken cancel) + { + cancel.ThrowIfCancellationRequested(); + + if (!_tracker.RecentlySeen(message.MessageId)) MessageReceived?.Invoke(this, message); + + return Task.CompletedTask; + } + + /// + public Task StartAsync() { return Task.CompletedTask; } + + /// + public Task StopAsync() { return Task.CompletedTask; } + } +} diff --git a/src/Lib.P2P/PubSub/PubSubMessage.cs b/src/Lib.P2P/PubSub/PubSubMessage.cs new file mode 100644 index 0000000000..77fad0a744 --- /dev/null +++ b/src/Lib.P2P/PubSub/PubSubMessage.cs @@ -0,0 +1,71 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using ProtoBuf; + +namespace Lib.P2P.PubSub +{ + /// + /// The PubSub message exchanged between peers. + /// + /// + [ProtoContract] + public class PubSubMessage + { + /// + /// Sequence of topic subscriptions of the sender. + /// + [ProtoMember(1)] + public Subscription[] Subscriptions; + + /// + /// Sequence of topic messages. + /// + [ProtoMember(2)] + public PublishedMessage[] PublishedMessages; + } + + /// + /// A peer's subscription to a topic. + /// + /// + [ProtoContract] + public class Subscription + { + /// + /// Determines if the topic is subscribed to. + /// + /// + /// true if subscribing; otherwise, false if + /// unsubscribing. + /// + [ProtoMember(1)] + public bool Subscribe; + + /// + /// The topic name/id. + /// + [ProtoMember(2)] + public string Topic; + } +} diff --git a/src/Lib.P2P/PubSub/PubSubService.cs b/src/Lib.P2P/PubSub/PubSubService.cs new file mode 100644 index 0000000000..0c1a42f36e --- /dev/null +++ b/src/Lib.P2P/PubSub/PubSubService.cs @@ -0,0 +1,280 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; + +namespace Lib.P2P.PubSub +{ + /// + /// A simple pub/sub messaging service that supports + /// multiple message routers. + /// + /// + /// Relies upon the router(s) to deliver and receive messages from other peers. + /// + public sealed class PubSubService : IPubSubService + { + private static ILog _log = LogManager.GetLogger(typeof(PubSubService)); + + private sealed class TopicHandler + { + public string Topic; + public Action Handler; + } + + private long _nextSequenceNumber; + private ConcurrentDictionary _topicHandlers; + private readonly MessageTracker _tracker = new(); + + // TODO: A general purpose CancellationTokenSource that stops publishing of + // messages when this service is stopped. + + /// + /// The local peer. + /// + public Peer LocalPeer { get; set; } + + /// + /// Sends and receives messages to other peers. + /// + public List Routers { get; set; } + + /// + /// The number of messages that have published. + /// + public ulong MesssagesPublished; + + /// + /// The number of messages that have been received. + /// + public ulong MesssagesReceived; + + /// + /// The number of duplicate messages that have been received. + /// + public ulong DuplicateMesssagesReceived; + + public PubSubService(Peer localPeer, IEnumerable messageRouters) + { + LocalPeer = localPeer; + Routers = messageRouters.ToList(); + } + + /// + public async Task StartAsync() + { + _topicHandlers = new ConcurrentDictionary(); + + // Resolution of 100 nanoseconds. + _nextSequenceNumber = DateTime.UtcNow.Ticks; + + // Init the stats. + MesssagesPublished = 0; + MesssagesReceived = 0; + DuplicateMesssagesReceived = 0; + + // Listen to the routers. + foreach (var router in Routers) + { + router.MessageReceived += Router_MessageReceived; + await router.StartAsync().ConfigureAwait(false); + } + } + + /// + public async Task StopAsync() + { + _topicHandlers.Clear(); + + foreach (var router in Routers) + { + router.MessageReceived -= Router_MessageReceived; + await router.StopAsync(); + } + } + + /// + /// Creates a message for the topic and data. + /// + /// + /// The topic name/id. + /// + /// + /// The payload of message. + /// + /// + /// A unique published message. + /// + /// + /// The is a monitonically + /// increasing unsigned long. + /// + public PublishedMessage CreateMessage(string topic, byte[] data) + { + var next = Interlocked.Increment(ref _nextSequenceNumber); + var seqno = BitConverter.GetBytes(next); + if (BitConverter.IsLittleEndian) + { + seqno = seqno.Reverse().ToArray(); + } + + return new PublishedMessage + { + Topics = new[] { topic }, + Sender = LocalPeer, + SequenceNumber = seqno, + DataBytes = data + }; + } + + /// + public Task> SubscribedTopicsAsync(CancellationToken cancel = default) + { + var topics = _topicHandlers.Values + .Select(t => t.Topic) + .Distinct(); + return Task.FromResult(topics); + } + + /// + public Task> PeersAsync(string topic = null, + CancellationToken cancel = default) + { + var peers = Routers + .SelectMany(r => r.InterestedPeers(topic)) + .Distinct(); + return Task.FromResult(peers); + } + + /// + public Task PublishAsync(string topic, string message, CancellationToken cancel = default) + { + return PublishAsync(topic, Encoding.UTF8.GetBytes(message), cancel); + } + + /// + public Task PublishAsync(string topic, Stream message, CancellationToken cancel = default) + { + using (MemoryStream ms = new()) + { +#pragma warning disable VSTHRD103 + message.CopyTo(ms); +#pragma warning disable VSTHRD103 + return PublishAsync(topic, ms.ToArray(), cancel); + } + } + + /// + public Task PublishAsync(string topic, byte[] message, CancellationToken cancel = default) + { + var msg = CreateMessage(topic, message); + ++MesssagesPublished; + return Task.WhenAll(Routers.Select(r => r.PublishAsync(msg, cancel))); + } + + /// + public async Task SubscribeAsync(string topic, + Action handler, + CancellationToken cancellationToken) + { + var topicHandler = new TopicHandler { Topic = topic, Handler = handler }; + _topicHandlers.TryAdd(topicHandler, topicHandler); + + // TODO: need a better way. +#pragma warning disable VSTHRD101 + cancellationToken.Register(async () => + { + _topicHandlers.TryRemove(topicHandler, out _); + + if (_topicHandlers.Values.Count(t => t.Topic == topic) == 0) + { + await Task.WhenAll(Routers.Select(r => r.LeaveTopicAsync(topic, CancellationToken.None))) + .ConfigureAwait(false); + } + }); +#pragma warning restore VSTHRD101 + + // Tell routers if first time. + Task Selector(IMessageRouter r) => r.JoinTopicAsync(topic, CancellationToken.None); + + if (_topicHandlers.Values.Count(t => t.Topic == topic) == 1) + { + await Task.WhenAll(Routers.Select(Selector)) + .ConfigureAwait(false); + } + } + + /// + /// Invoked when a router gets a message. + /// + /// + /// The . + /// + /// + /// The message. + /// + /// + /// Invokes any topic handlers and publishes the messages on the other routers. + /// + private void Router_MessageReceived(object sender, PublishedMessage msg) + { + ++MesssagesReceived; + + // Check for duplicate message. + if (_tracker.RecentlySeen(msg.MessageId)) + { + ++DuplicateMesssagesReceived; + return; + } + + // Call local topic handlers. + var handlers = _topicHandlers.Values + .Where(th => msg.Topics.Contains(th.Topic)); + foreach (var handler in handlers) + { + try + { + handler.Handler(msg); + } + catch (Exception e) + { + _log.Error($"Topic handler for '{handler.Topic}' failed.", e); + } + } + + // Tell other message routers. + _ = Task.WhenAll(Routers + .Where(r => r != sender) + .Select(r => r.PublishAsync(msg, CancellationToken.None)) + ); + } + } +} diff --git a/src/Lib.P2P/PubSub/PublishedMessage.cs b/src/Lib.P2P/PubSub/PublishedMessage.cs new file mode 100644 index 0000000000..197faffa49 --- /dev/null +++ b/src/Lib.P2P/PubSub/PublishedMessage.cs @@ -0,0 +1,97 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using MultiFormats; +using ProtoBuf; + +namespace Lib.P2P.PubSub +{ + /// + /// A published messaged for a topic(s). + /// + /// + /// + /// TODO: Sender should really be called Author. + /// + /// + [ProtoContract] + public sealed class PublishedMessage : IPublishedMessage + { + private string _messageId; + + /// + public Peer Sender { get; set; } + + /// + /// Who sent the the message. + /// + public Peer Forwarder { get; set; } + + [ProtoMember(1)] + private byte[] From { get => Sender?.Id.ToArray(); set => Sender = new Peer {Id = new MultiHash(value)}; } + + /// + [ProtoMember(4)] + public IEnumerable Topics { get; set; } + + /// + [ProtoMember(3)] + public byte[] SequenceNumber { get; set; } + + /// + [ProtoMember(2)] + public byte[] DataBytes { get; set; } + + /// + public Stream DataStream => new MemoryStream(DataBytes, false); + + /// > + /// NOT SUPPORTED, use . + /// + /// + /// A published message does not have a content id. + /// + public Cid Id => throw new NotSupportedException(); + + /// + /// A universally unique id for the message. + /// + /// + /// The sender's ID concatenated with the . + /// + public string MessageId + { + get + { + if (_messageId == null) _messageId = Sender.Id.ToBase58() + SequenceNumber.ToHexString(); + return _messageId; + } + } + + /// + public long Size => DataBytes.Length; + } +} diff --git a/src/Lib.P2P/PubSub/TopicManager.cs b/src/Lib.P2P/PubSub/TopicManager.cs new file mode 100644 index 0000000000..76ece51645 --- /dev/null +++ b/src/Lib.P2P/PubSub/TopicManager.cs @@ -0,0 +1,140 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; + +namespace Lib.P2P.PubSub +{ + /// + /// Maintains the sequence of peer's that are interested in a topic. + /// + public class TopicManager + { + private static readonly IEnumerable Nopeers = Enumerable.Empty(); + + private ConcurrentDictionary> _topics = new(); + + /// + /// Get the peers interested in a topic. + /// + /// + /// The topic of interest or null for all topics. + /// + /// + /// A sequence of that are interested + /// in the . + /// + public IEnumerable GetPeers(string topic) + { + if (topic == null) return _topics.Values.SelectMany(v => v); + + if (!_topics.TryGetValue(topic, out var peers)) return Nopeers; + return peers; + } + + /// + /// Gets the topics that a peer is interested in + /// + /// + /// The . + /// + /// + /// A sequence of topics that the is + /// interested in. + /// + public IEnumerable GetTopics(Peer peer) + { + return _topics + .Where(kp => kp.Value.Contains(peer)) + .Select(kp => kp.Key); + } + + /// + /// Indicate that the is interested in the + /// topic. + /// + /// + /// The topic of interest. + /// + /// + /// A + /// + /// + /// Duplicates are ignored. + /// + public void AddInterest(string topic, Peer peer) + { + _topics.AddOrUpdate( + topic, + (key) => new HashSet {peer}, + (key, peers) => + { + peers.Add(peer); + return peers; + }); + } + + /// + /// Indicate that the is not interested in the + /// topic. + /// + /// + /// The topic of interest. + /// + /// + /// A + /// + public void RemoveInterest(string topic, Peer peer) + { + _topics.AddOrUpdate( + topic, + (key) => new HashSet(), + (key, list) => + { + list.Remove(peer); + return list; + }); + } + + /// + /// Indicates that the peer is not interested in anything. + /// + /// + /// The .s + /// + public void Clear(Peer peer) + { + foreach (var topic in _topics.Keys) + { + RemoveInterest(topic, peer); + } + } + + /// + /// Remove all topics. + /// + public void Clear() { _topics.Clear(); } + } +} diff --git a/src/Lib.P2P/README.md b/src/Lib.P2P/README.md new file mode 100644 index 0000000000..13f331a007 --- /dev/null +++ b/src/Lib.P2P/README.md @@ -0,0 +1,23 @@ +# Peer Talk + +[![build status](https://ci.appveyor.com/api/projects/status/github/richardschneider/peer-talk?branch=master&svg=true)](https://ci.appveyor.com/project/richardschneider/peer-talk) +[![travis build](https://travis-ci.org/richardschneider/peer-talk.svg?branch=master)](https://travis-ci.org/richardschneider/peer-talk) +[![CircleCI](https://circleci.com/gh/richardschneider/peer-talk.svg?style=svg)](https://circleci.com/gh/richardschneider/peer-talk) +[![Coverage Status](https://coveralls.io/repos/richardschneider/peer-talk/badge.svg?branch=master&service=github)](https://coveralls.io/github/richardschneider/peer-talk?branch=master) +[![Version](https://img.shields.io/nuget/v/PeerTalk.svg)](https://www.nuget.org/packages/PeerTalk) +[![docs](https://richardschneider.github.io/peer-talk/images/docs-latest-green.svg)](https://richardschneider.github.io/peer-talk/articles/intro.html) + +*Peer Talk* is a collection of peer-to-peer protocols in the spirit of [libp2p](https://github.com/libp2p/libp2p). + +## Related projects + +- [IPFS Core](https://github.com/richardschneider/net-ipfs-core) +- [IPFS Engine](https://github.com/richardschneider/net-ipfs-engine) + +# License +Copyright 2018 Richard Schneider (makaretu@gmail.com) + +Peer Talk is licensed under the [MIT](http://www.opensource.org/licenses/mit-license.php "Read more about the MIT license form") license. Refer to the [LICENSE](https://github.com/richardschneider/peer-talk/blob/master/LICENSE) file for more information. + +Buy Me A Coffee + diff --git a/src/Lib.P2P/Routing/ContentRouter.cs b/src/Lib.P2P/Routing/ContentRouter.cs new file mode 100644 index 0000000000..aafb704dbb --- /dev/null +++ b/src/Lib.P2P/Routing/ContentRouter.cs @@ -0,0 +1,144 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using MultiFormats; + +namespace Lib.P2P.Routing +{ + /// + /// Manages a list of content that is provided by multiple peers. + /// + /// + /// A peer is expected to provide content for at least . + /// After this expires the provider is removed from the list. + /// + public sealed class ContentRouter : IDisposable + { + private sealed class ProviderInfo + { + /// + /// When the provider entry expires. + /// + public DateTime Expiry { get; set; } + + /// + /// The peer ID of the provider. + /// + public MultiHash PeerId { get; set; } + } + + private ConcurrentDictionary> _content = + new(); + + private string Key(Cid cid) { return "/providers/" + cid.Hash.ToBase32(); } + + /// + /// How long a provider is assumed to provide some content. + /// + /// + /// Defaults to 24 hours (1 day). + /// + public TimeSpan ProviderTtl { get; set; } = TimeSpan.FromHours(24); + + /// + /// Adds the and to the content routing system. + /// + /// + /// The ID of some content that the contains. + /// + /// + /// The peer ID that contains the . + /// + public void Add(Cid cid, MultiHash provider) { Add(cid, provider, DateTime.Now); } + + /// + /// Adds the and to the content + /// routing system at the specified . + /// + /// + /// The ID of some content that the contains. + /// + /// + /// The peer ID that contains the . + /// + /// + /// The local time that the started to provide + /// the . + /// + public void Add(Cid cid, MultiHash provider, DateTime now) + { + var pi = new ProviderInfo + { + Expiry = now + ProviderTtl, + PeerId = provider + }; + + _content.AddOrUpdate( + Key(cid), + (key) => new List {pi}, + (key, providers) => + { + var existing = providers + .FirstOrDefault(p => p.PeerId == provider); + if (existing != null) + { + existing.Expiry = pi.Expiry; + } + else + { + providers.Add(pi); + } + + return providers; + }); + } + + /// + /// Gets the providers for the . + /// + /// + /// The ID of some content. + /// + /// + /// A sequence of peer IDs (providers) that contain the . + /// + public IEnumerable Get(Cid cid) + { + if (!_content.TryGetValue(Key(cid), out var providers)) + { + return Enumerable.Empty(); + } + + return providers + .Where(p => DateTime.Now < p.Expiry) + .Select(p => p.PeerId); + } + + /// + public void Dispose() { } + } +} diff --git a/src/Lib.P2P/Routing/DhtMessages.cs b/src/Lib.P2P/Routing/DhtMessages.cs new file mode 100644 index 0000000000..70777745bb --- /dev/null +++ b/src/Lib.P2P/Routing/DhtMessages.cs @@ -0,0 +1,248 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Linq; +using MultiFormats; +using ProtoBuf; + +namespace Lib.P2P.Routing +{ + // From https://github.com/libp2p/js-libp2p-kad-dht/blob/master/src/message/dht.proto.js\ + // and https://github.com/libp2p/go-libp2p-kad-dht/blob/master/pb/dht.proto + + /// + /// TODO + /// + [ProtoContract] + public class DhtRecordMessage + { + /// + /// TODO + /// + [ProtoMember(1)] + public byte[] Key { get; set; } + + /// + /// TODO + /// + [ProtoMember(2)] + public byte[] Value { get; set; } + + /// + /// TODO + /// + [ProtoMember(3)] + public byte[] Author { get; set; } + + /// + /// TODO + /// + [ProtoMember(4)] + public byte[] Signature { get; set; } + + /// + /// TODO + /// + [ProtoMember(5)] + public string TimeReceived { get; set; } + } + + /// + /// The type of DHT/KAD message. + /// + public enum MessageType + { + /// + /// Put a value. + /// + PutValue = 0, + + /// + /// Get a value. + /// + GetValue = 1, + + /// + /// Indicate that a peer can provide something. + /// + AddProvider = 2, + + /// + /// Get the providers for something. + /// + GetProviders = 3, + + /// + /// Find a peer. + /// + FindNode = 4, + + /// + /// NYI + /// + Ping = 5 + } + + /// + /// The connection status. + /// + public enum ConnectionType + { + /// + /// Sender does not have a connection to peer, and no extra information (default) + /// + NotConnected = 0, + + /// + /// Sender has a live connection to peer + /// + Connected = 1, + + /// + /// Sender recently connected to peer + /// + CanConnect = 2, + + /// + /// Sender recently tried to connect to peer repeatedly but failed to connect + /// ("try" here is loose, but this should signal "made strong effort, failed") + /// + CannotConnect = 3 + } + + /// + /// Information about a peer. + /// + [ProtoContract] + public class DhtPeerMessage + { + /// + /// ID of a given peer. + /// + /// + /// The as a byte array, + /// + [ProtoMember(1)] + public byte[] Id { get; set; } + + /// + /// Addresses for a given peer + /// + /// + /// A sequence of as a byte array. + /// + [ProtoMember(2)] + public byte[][] Addresses { get; set; } + + /// + /// used to signal the sender's connection capabilities to the peer + /// + [ProtoMember(3)] + public ConnectionType Connection { get; set; } + + /// + /// Convert the message into a . + /// + /// + /// + public bool TryToPeer(out Peer peer) + { + peer = null; + + // Sanity checks. + if (Id == null || Id.Length == 0) + return false; + + MultiHash id = new(Id); + peer = new Peer + { + Id = id + }; + if (Addresses != null) + { + MultiAddress x = new($"/ipfs/{id}"); + peer.Addresses = Addresses + .Select(bytes => + { + try + { + MultiAddress ma = new(bytes); + ma.Protocols.AddRange(x.Protocols); + return ma; + } + catch + { + return null; + } + }) + .Where(a => a != null) + .ToArray(); + } + + return true; + } + } + + /// + /// The DHT message exchanged between peers. + /// + [ProtoContract] + public class DhtMessage + { + /// + /// What type of message it is. + /// + [ProtoMember(1)] + public MessageType Type { get; set; } + + /// + /// Coral cluster level. + /// + [ProtoMember(10)] + public int ClusterLevelRaw { get; set; } + + /// + /// TODO + /// + [ProtoMember(2)] + public byte[] Key { get; set; } + + /// + /// TODO + /// + [ProtoMember(3)] + public DhtRecordMessage Record { get; set; } + + /// + /// The closer peers for a query. + /// + [ProtoMember(8)] + public DhtPeerMessage[] CloserPeers { get; set; } + + /// + /// The providers for a query. + /// + [ProtoMember(9)] + public DhtPeerMessage[] ProviderPeers { get; set; } + } +} diff --git a/src/Lib.P2P/Routing/DhtService.cs b/src/Lib.P2P/Routing/DhtService.cs new file mode 100644 index 0000000000..6327abbd88 --- /dev/null +++ b/src/Lib.P2P/Routing/DhtService.cs @@ -0,0 +1,444 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using MultiFormats; +using ProtoBuf; +using Semver; + +namespace Lib.P2P.Routing +{ + /// + /// DHT Protocol version 1.0 + /// + public class DhtService : IDhtService + { + private static ILog _log = LogManager.GetLogger(typeof(DhtService)); + + /// + public virtual string Name { get; } = "libp2p-cs/kad"; + + /// + public SemVersion Version { get; } = new(1); + + /// + /// Provides access to other peers. + /// + public ISwarmService SwarmService { get; set; } + + /// + /// Routing information on peers. + /// + public RoutingTable RoutingTable { private set; get; } + + /// + /// Peers that can provide some content. + /// + public ContentRouter ContentRouter { private set; get; } + + /// + /// The number of closer peers to return. + /// + /// + /// Defaults to 20. + /// + public int CloserPeerCount { get; set; } = 20; + + /// + /// Raised when the DHT is stopped. + /// + /// + public event EventHandler Stopped; + + /// + public override string ToString() { return $"/{Name}/{Version}"; } + + /// + public async Task ProcessMessageAsync(PeerConnection connection, + Stream stream, + CancellationToken cancel = default) + { + while (true) + { + var request = await ProtoBufHelper.ReadMessageAsync(stream, cancel).ConfigureAwait(false); + + _log.Debug($"got {request.Type} from {connection.RemotePeer}"); + var response = new DhtMessage + { + Type = request.Type, + ClusterLevelRaw = request.ClusterLevelRaw + }; + switch (request.Type) + { + case MessageType.Ping: + response = ProcessPing(request, response); + break; + case MessageType.FindNode: + response = ProcessFindNode(request, response); + break; + case MessageType.GetProviders: + response = ProcessGetProviders(request, response); + break; + case MessageType.AddProvider: + response = ProcessAddProvider(connection.RemotePeer, request, response); + break; + case MessageType.PutValue: + break; + case MessageType.GetValue: + break; + default: + _log.Debug($"unknown {request.Type} from {connection.RemotePeer}"); + + // TODO: Should we close the stream? + continue; + } + + if (response == null) + { + continue; + } + + Serializer.SerializeWithLengthPrefix(stream, response, PrefixStyle.Base128); + await stream.FlushAsync(cancel).ConfigureAwait(false); + } + } + + /// + public Task StartAsync() + { + _log.Debug("Starting"); + + RoutingTable = new RoutingTable(SwarmService.LocalPeer); + ContentRouter = new ContentRouter(); + SwarmService.AddProtocol(this); + SwarmService.PeerDiscovered += Swarm_PeerDiscovered; + SwarmService.PeerRemoved += Swarm_PeerRemoved; + + foreach (var peer in SwarmService.KnownPeers) + { + RoutingTable.Add(peer); + } + + return Task.CompletedTask; + } + + /// + public Task StopAsync() + { + _log.Debug("Stopping"); + + SwarmService.RemoveProtocol(this); + SwarmService.PeerDiscovered -= Swarm_PeerDiscovered; + SwarmService.PeerRemoved -= Swarm_PeerRemoved; + + Stopped?.Invoke(this, EventArgs.Empty); + ContentRouter?.Dispose(); + return Task.CompletedTask; + } + + /// + /// The swarm has discovered a new peer, update the routing table. + /// + private void Swarm_PeerDiscovered(object sender, Peer e) + { + RoutingTable.Add(e); + } + + /// + /// The swarm has removed a peer, update the routing table. + /// + private void Swarm_PeerRemoved(object sender, Peer e) + { + RoutingTable.Remove(e); + } + + /// + public async Task FindPeerAsync(MultiHash id, CancellationToken cancel = default) + { + // Can always find self. + if (SwarmService.LocalPeer.Id == id) + { + return SwarmService.LocalPeer; + } + + // Maybe the swarm knows about it. + var found = SwarmService.KnownPeers.FirstOrDefault(p => p.Id == id); + if (found != null && found.Addresses.Any()) + { + return found; + } + + // Ask our peers for information on the requested peer. + var dQuery = new DistributedQuery + { + QueryType = MessageType.FindNode, + QueryKey = id, + Dht = this, + AnswersNeeded = 1 + }; + + await dQuery.RunAsync(cancel).ConfigureAwait(false); + + // If not found, return the closest peer. + return !dQuery.Answers.Any() ? RoutingTable.NearestPeers(id).FirstOrDefault() : dQuery.Answers.First(); + } + + /// + public Task ProvideAsync(Cid cid, bool advertise = true, CancellationToken cancel = default) + { + ContentRouter.Add(cid, SwarmService.LocalPeer.Id); + if (advertise) + { + Advertise(cid); + } + + return Task.CompletedTask; + } + + /// + public async Task> FindProvidersAsync(Cid id, + int limit = 20, + Action action = null, + CancellationToken cancel = default) + { + var dQuery = new DistributedQuery + { + QueryType = MessageType.GetProviders, + QueryKey = id.Hash, + Dht = this, + AnswersNeeded = limit, + }; + + if (action != null) + { + dQuery.AnswerObtained += (s, e) => action.Invoke(e); + } + + // Add any providers that we already know about. + var providers = ContentRouter + .Get(id) + .Select(pid => pid == SwarmService.LocalPeer.Id + ? SwarmService.LocalPeer + : SwarmService.RegisterPeer(new Peer {Id = pid})); + + foreach (var provider in providers) + { + dQuery.AddAnswer(provider); + } + + // Ask our peers for more providers. + if (limit > dQuery.Answers.Count()) + { + await dQuery.RunAsync(cancel).ConfigureAwait(false); + } + + return dQuery.Answers.Take(limit); + } + + /// + /// Advertise that we can provide the CID to the X closest peers + /// of the CID. + /// + /// + /// The CID to advertise.ipfs + /// + /// + /// This starts a background process to send the AddProvider message + /// to the 4 closest peers to the . + /// + public void Advertise(Cid cid) + { + _ = Task.Run(async () => + { + var advertsNeeded = 4; + var message = new DhtMessage + { + Type = MessageType.AddProvider, + Key = cid.Hash.ToArray(), + ProviderPeers = new[] + { + new DhtPeerMessage + { + Id = SwarmService.LocalPeer.Id.ToArray(), + Addresses = SwarmService.LocalPeer.Addresses + .Select(a => a.WithoutPeerId().ToArray()) + .ToArray() + } + } + }; + + var peers = RoutingTable + .NearestPeers(cid.Hash) + .Where(p => p != SwarmService.LocalPeer); + + foreach (var peer in peers) + { + try + { + await using (var stream = await SwarmService.DialAsync(peer, ToString())) + { + Serializer.SerializeWithLengthPrefix(stream, message, PrefixStyle.Base128); + await stream.FlushAsync(); + } + + if (--advertsNeeded == 0) + { + break; + } + } + catch (Exception) + { + // eat it. This is fire and forget. + } + } + }); + } + + /// + /// Process a ping request. + /// + /// + /// Simply return the . + /// + private static DhtMessage ProcessPing(DhtMessage request, DhtMessage response) { return request; } + + /// + /// Process a find node request. + /// + public DhtMessage ProcessFindNode(DhtMessage request, DhtMessage response) + { + // Some random walkers generate a random Key that is not hashed. + MultiHash peerId; + try + { + peerId = new MultiHash(request.Key); + } + catch (Exception) + { + _log.Error($"Bad FindNode request key {request.Key.ToHexString()}"); + peerId = MultiHash.ComputeHash(request.Key); + } + + // Do we know the peer?. + var found = SwarmService.LocalPeer.Id == peerId ? SwarmService.LocalPeer : SwarmService.KnownPeers.FirstOrDefault(p => p.Id == peerId); + + // Find the closer peers. + List closerPeers = new(); + if (found != null) + { + closerPeers.Add(found); + } + else + { + closerPeers.AddRange(RoutingTable.NearestPeers(peerId).Take(CloserPeerCount)); + } + + // Build the response. + response.CloserPeers = closerPeers + .Select(peer => new DhtPeerMessage + { + Id = peer.Id.ToArray(), + Addresses = peer.Addresses.Select(a => a.WithoutPeerId().ToArray()).ToArray() + }) + .ToArray(); + + if (_log.IsDebugEnabled) + { + _log.Debug($"returning {response.CloserPeers.Length.ToString()} closer peers"); + } + + return response; + } + + /// + /// Process a get provider request. + /// + public DhtMessage ProcessGetProviders(DhtMessage request, DhtMessage response) + { + // Find providers for the content. + var cid = new Cid {Hash = new MultiHash(request.Key)}; + response.ProviderPeers = ContentRouter + .Get(cid) + .Select(pid => + { + var peer = pid == SwarmService.LocalPeer.Id + ? SwarmService.LocalPeer + : SwarmService.RegisterPeer(new Peer {Id = pid}); + return new DhtPeerMessage + { + Id = peer.Id.ToArray(), + Addresses = peer.Addresses.Select(a => a.WithoutPeerId().ToArray()).ToArray() + }; + }) + .Take(20) + .ToArray(); + + // Also return the closest peers + return ProcessFindNode(request, response); + } + + /// + /// Process an add provider request. + /// + public DhtMessage ProcessAddProvider(Peer remotePeer, DhtMessage request, DhtMessage response) + { + if (request.ProviderPeers == null) + { + return null; + } + + Cid cid; + try + { + cid = new Cid {Hash = new MultiHash(request.Key)}; + } + catch (Exception) + { + _log.Error($"Bad AddProvider request key {request.Key.ToHexString()}"); + return null; + } + + var providers = request.ProviderPeers + .Select(p => p.TryToPeer(out var peer) ? peer : (Peer)null) + .Where(p => p != null) + .Where(p => p == remotePeer) + .Where(p => p.Addresses.Any()) + .Where(p => SwarmService.IsAllowed(p)); + + foreach (var provider in providers) + { + SwarmService.RegisterPeer(provider); + ContentRouter.Add(cid, provider.Id); + } + + // There is no response for this request. + return null; + } + } +} diff --git a/src/Lib.P2P/Routing/DistributedQuery.cs b/src/Lib.P2P/Routing/DistributedQuery.cs new file mode 100644 index 0000000000..356f24caf3 --- /dev/null +++ b/src/Lib.P2P/Routing/DistributedQuery.cs @@ -0,0 +1,318 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using Common.Logging; +using MultiFormats; +using ProtoBuf; +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Lib.P2P.Routing +{ + /// + /// A query that is sent to multiple peers. + /// + /// + /// The type of answer returned by a peer. + /// + public class DistributedQuery where T : class + { + private static readonly ILog Log = LogManager.GetLogger("Lib.P2P.Routing.DistributedQuery"); + private static int _nextQueryId = 1; + + /// + /// The maximum number of peers that can be queried at one time + /// for all distributed queries. + /// + private static readonly SemaphoreSlim AskCount = new(128); + + /// + /// The maximum time spent on waiting for an answer from a peer. + /// + private static readonly TimeSpan AskTime = TimeSpan.FromSeconds(10); + + /// + /// Controls the running of the distributed query. + /// + /// + /// Becomes cancelled when the correct number of answers are found + /// or the caller of wants to cancel + /// or the DHT is stopped. + /// + private CancellationTokenSource _runningQuery; + + private readonly ConcurrentDictionary _visited = new(); + private readonly ConcurrentDictionary _answers = new(); + private DhtMessage _queryMessage; + private int _failedConnects; + + /// + /// Raised when an answer is obtained. + /// + public event EventHandler AnswerObtained; + + /// + /// The unique identifier of the query. + /// + public int Id { get; } = _nextQueryId++; + + /// + /// The received answers for the query. + /// + public IEnumerable Answers => _answers.Values; + + /// + /// The number of answers needed. + /// + /// + /// When the numbers reaches this limit + /// the running query will stop. + /// + internal int AnswersNeeded { get; set; } = 1; + + /// + /// The maximum number of concurrent peer queries to perform + /// for one distributed query. + /// + /// + /// The default is 16. + /// + /// + /// The number of peers that are asked for the answer. + /// + public int ConcurrencyLevel { get; set; } = 16; + + /// + /// The distributed hash table. + /// + public IDhtService Dht { get; set; } + + /// + /// The type of query to perform. + /// + internal MessageType QueryType { get; set; } + + /// + /// The key to find. + /// + internal MultiHash QueryKey { get; set; } + + /// + /// Starts the distributed query. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + public async Task RunAsync(CancellationToken cancel) + { + Log.Debug($"Q{Id} run {QueryType} {QueryKey}"); + + _runningQuery = CancellationTokenSource.CreateLinkedTokenSource(cancel); + Dht.Stopped += OnDhtStopped; + _queryMessage = new DhtMessage + { + Type = QueryType, + Key = QueryKey?.ToArray(), + }; + + var tasks = Enumerable + .Range(1, ConcurrencyLevel) + .Select(i => { var id = i; return AskAsync(id); }); + try + { + await Task.WhenAll(tasks).ConfigureAwait(false); + } + catch (Exception) + { + // eat it + } + finally + { + Dht.Stopped -= OnDhtStopped; + } + Log.Debug($"Q{Id} found {_answers.Count} answers, visited {_visited.Count} peers, failed {_failedConnects}"); + } + + private void OnDhtStopped(object sender, EventArgs e) + { + Log.Debug($"Q{Id} cancelled because DHT stopped."); + _runningQuery.Cancel(); + } + + /// + /// Ask the next peer the question. + /// + public async Task AskAsync(int taskId) + { + var pass = 0; + var waits = 20; + while (!_runningQuery.IsCancellationRequested && waits > 0) + { + // Get the nearest peer that has not been visited. + var peer = Dht.RoutingTable + .NearestPeers(QueryKey) + .Where(p => !_visited.ContainsKey(p)) + .FirstOrDefault(); + if (peer == null) + { + --waits; + await Task.Delay(100); + continue; + } + + if (!_visited.TryAdd(peer, peer)) + { + continue; + } + ++pass; + + // Ask the nearest peer. + await AskCount.WaitAsync(_runningQuery.Token).ConfigureAwait(false); + var start = DateTime.Now; + Log.Debug($"Q{Id}.{taskId}.{pass} ask {peer}"); + try + { + using (CancellationTokenSource timeout = new(AskTime)) + using (var cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, _runningQuery.Token)) + using (var stream = await Dht.SwarmService.DialAsync(peer, Dht.ToString(), cts.Token).ConfigureAwait(false)) + { + // Send the KAD query and get a response. + Serializer.SerializeWithLengthPrefix(stream, _queryMessage, PrefixStyle.Base128); + await stream.FlushAsync(cts.Token).ConfigureAwait(false); + var response = await ProtoBufHelper.ReadMessageAsync(stream, cts.Token).ConfigureAwait(false); + + // Process answer + ProcessProviders(response.ProviderPeers); + ProcessCloserPeers(response.CloserPeers); + } + var time = DateTime.Now - start; + Log.Debug($"Q{Id}.{taskId}.{pass} ok {peer} ({time.TotalMilliseconds} ms)"); + } + catch (Exception e) + { + Interlocked.Increment(ref _failedConnects); + var time = DateTime.Now - start; + Log.Warn($"Q{Id}.{taskId}.{pass} failed ({time.TotalMilliseconds} ms) - {e.Message}"); + // eat it + } + finally + { + AskCount.Release(); + } + } + } + + private void ProcessProviders(DhtPeerMessage[] providers) + { + if (providers == null) + { + return; + } + + foreach (var provider in providers) + { + if (provider.TryToPeer(out Peer p)) + { + if (p == Dht.SwarmService.LocalPeer || !Dht.SwarmService.IsAllowed(p)) + { + continue; + } + + p = Dht.SwarmService.RegisterPeer(p); + if (QueryType == MessageType.GetProviders) + { + // Only unique answers + var answer = p as T; + if (!_answers.ContainsKey(answer)) + { + AddAnswer(answer); + } + } + } + } + } + + private void ProcessCloserPeers(DhtPeerMessage[] closerPeers) + { + if (closerPeers == null) + { + return; + } + + foreach (var closer in closerPeers) + { + if (closer.TryToPeer(out Peer p)) + { + if (p == Dht.SwarmService.LocalPeer || !Dht.SwarmService.IsAllowed(p)) + { + continue; + } + + p = Dht.SwarmService.RegisterPeer(p); + if (QueryType == MessageType.FindNode && QueryKey == p.Id) + { + AddAnswer(p as T); + } + } + } + } + + /// + /// Add a answer to the query. + /// + /// + /// An answer. + /// + /// + /// + internal void AddAnswer(T answer) + { + if (answer == null) + { + return; + } + + if (_runningQuery != null && _runningQuery.IsCancellationRequested) + { + return; + } + + if (_answers.TryAdd(answer, answer)) + { + if (_answers.Count >= AnswersNeeded && _runningQuery != null && !_runningQuery.IsCancellationRequested) + { + _runningQuery.Cancel(false); + } + } + + AnswerObtained?.Invoke(this, answer); + } + } +} diff --git a/src/Lib.P2P/Routing/IContentRouting.cs b/src/Lib.P2P/Routing/IContentRouting.cs new file mode 100644 index 0000000000..d00ee0d9ca --- /dev/null +++ b/src/Lib.P2P/Routing/IContentRouting.cs @@ -0,0 +1,81 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Lib.P2P.Routing +{ + /// + /// Find information about who has what content. + /// + /// + /// No IPFS documentation is currently available. See the + /// code. + /// + public interface IContentRouting + { + /// + /// Adds the to the content routing system. + /// + /// + /// The ID of some content that the peer contains. + /// + /// + /// Advertise the to other peers. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + Task ProvideAsync(Cid cid, bool advertise = true, CancellationToken cancel = default); + + /// + /// Find the providers for the specified content. + /// + /// + /// The of the content. + /// + /// + /// The maximum number of peers to return. Defaults to 20. + /// + /// + /// An action to perform when a provider is found. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation that returns + /// a sequence of IPFS . + /// + Task> FindProvidersAsync(Cid id, + int limit = 20, + Action providerFound = null, + CancellationToken cancel = default); + } +} diff --git a/src/Lib.P2P/Routing/IDhtService.cs b/src/Lib.P2P/Routing/IDhtService.cs new file mode 100644 index 0000000000..f4349f299a --- /dev/null +++ b/src/Lib.P2P/Routing/IDhtService.cs @@ -0,0 +1,86 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Lib.P2P.Protocols; + +namespace Lib.P2P.Routing +{ + /// + /// + /// + public interface IDhtService : IPeerProtocol, IService, IPeerRouting, IContentRouting + { + /// + /// Provides access to other peers. + /// + ISwarmService SwarmService { get; set; } + + RoutingTable RoutingTable { get; } + + /// + /// The number of closer peers to return. + /// + /// + /// Defaults to 20. + /// + int CloserPeerCount { get; set; } + + /// + /// Raised when the DHT is stopped. + /// + /// + event EventHandler Stopped; + + /// + string ToString(); + + /// + /// Advertise that we can provide the CID to the X closest peers + /// of the CID. + /// + /// + /// The CID to advertise.ipfs + /// + /// + /// This starts a background process to send the AddProvider message + /// to the 4 closest peers to the . + /// + void Advertise(Cid cid); + + /// + /// Process a find node request. + /// + DhtMessage ProcessFindNode(DhtMessage request, DhtMessage response); + + /// + /// Process a get provider request. + /// + DhtMessage ProcessGetProviders(DhtMessage request, DhtMessage response); + + /// + /// Process an add provider request. + /// + DhtMessage ProcessAddProvider(Peer remotePeer, DhtMessage request, DhtMessage response); + } +} diff --git a/src/Lib.P2P/Routing/IPeerRouting.cs b/src/Lib.P2P/Routing/IPeerRouting.cs new file mode 100644 index 0000000000..68aeef647e --- /dev/null +++ b/src/Lib.P2P/Routing/IPeerRouting.cs @@ -0,0 +1,54 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Threading; +using System.Threading.Tasks; +using MultiFormats; + +namespace Lib.P2P.Routing +{ + /// + /// Find information about a peer. + /// + /// + /// No IPFS documentation is currently available. See the + /// code. + /// + public interface IPeerRouting + { + /// + /// Information about an IPFS peer. + /// + /// + /// The ID of the IPFS peer. + /// + /// + /// Is used to stop the task. When cancelled, the is NOT raised. + /// + /// + /// A task that represents the asynchronous operation that returns + /// the information or a closer peer. + /// + Task FindPeerAsync(MultiHash id, CancellationToken cancel = default); + } +} diff --git a/src/Lib.P2P/Routing/RoutingTable.cs b/src/Lib.P2P/Routing/RoutingTable.cs new file mode 100644 index 0000000000..e8338bac73 --- /dev/null +++ b/src/Lib.P2P/Routing/RoutingTable.cs @@ -0,0 +1,116 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.Linq; +using Catalyst.KBucket; +using MultiFormats; + +namespace Lib.P2P.Routing +{ + internal class RoutingPeer : IContact + { + public Peer Peer; + + public RoutingPeer(Peer peer) { Peer = peer; } + + public byte[] Id => RoutingTable.Key(Peer.Id); + } + + /// + /// A wrapper around k-bucket, to provide easy store and retrival for peers. + /// + public class RoutingTable + { + private KBucket _peers = new(); + + /// + /// Creates a new instance of the for + /// the specified . + /// + /// + public RoutingTable(Peer localPeer) + { + _peers.LocalContactId = Key(localPeer.Id); + _peers.ContactsToPing = 1; + _peers.Ping += Peers_Ping; + } + + /// + /// A k-bucket is full! + /// + /// + /// Currently this just removes the oldest contact from the list, + /// without acutally pinging the individual peers. + /// + /// This is the same as go does, but should probably + /// be upgraded to actually ping the individual peers. + /// + private void Peers_Ping(object sender, PingEventArgs e) + { + if (_peers.Remove(e.Oldest.First())) _peers.Add(e.Newest); + } + + /// + /// Add some information about the peer. + /// + public void Add(Peer peer) { _peers.Add(new RoutingPeer(peer)); } + + /// + /// Remove the information about the peer. + /// + public void Remove(Peer peer) { _peers.Remove(new RoutingPeer(peer)); } + + /// + /// Determines in the peer exists in the routing table. + /// + public bool Contains(Peer peer) { return _peers.Contains(new RoutingPeer(peer)); } + + /// + /// Find the closest peers to the peer ID. + /// + public IEnumerable NearestPeers(MultiHash id) + { + return _peers + .Closest(Key(id)) + .Select(r => r.Peer); + } + + /// + /// Converts the peer ID to a routing table key. + /// + /// A multihash + /// + /// The routing table key. + /// + /// + /// The peer ID is actually a multihash, it always starts with the same characters + /// (ie, Qm for rsa). This causes the distribution of hashes to be + /// non-equally distributed across all possible hash buckets. So the re-hash + /// into a non-multihash is to evenly distribute the potential keys and + /// hash buckets. + /// + /// + public static byte[] Key(MultiHash id) { return MultiHash.ComputeHash(id.ToArray()).Digest; } + } +} diff --git a/src/Lib.P2P/SecureCommunication/Psk1Protector.cs b/src/Lib.P2P/SecureCommunication/Psk1Protector.cs new file mode 100644 index 0000000000..edbb82e338 --- /dev/null +++ b/src/Lib.P2P/SecureCommunication/Psk1Protector.cs @@ -0,0 +1,57 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Cryptography; + +namespace Lib.P2P.SecureCommunication +{ + /// + /// Provides access to a private network of peers that + /// uses a . + /// + /// + /// The calls the network protector whenever a connection + /// is being established with another peer. + /// + /// + public class Psk1Protector : INetworkProtector + { + /// + /// The key of the private network. + /// + /// + /// Only peers with this key can be communicated with. + /// + public PreSharedKey Key { private get; set; } + + /// + public Task ProtectAsync(PeerConnection connection, + CancellationToken cancel = default) + { + return Task.FromResult(new Psk1Stream(connection.Stream, Key)); + } + } +} diff --git a/src/Lib.P2P/SecureCommunication/Psk1Stream.cs b/src/Lib.P2P/SecureCommunication/Psk1Stream.cs new file mode 100644 index 0000000000..ecc75cb9fd --- /dev/null +++ b/src/Lib.P2P/SecureCommunication/Psk1Stream.cs @@ -0,0 +1,203 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Cryptography; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Parameters; + +namespace Lib.P2P.SecureCommunication +{ + /// + /// A duplex stream that is encrypted with a . + /// + /// + /// The XSalsa20 cipher is used to encrypt the data. + /// + /// + public class Psk1Stream : Stream + { + private const int KeyBitLength = 256; + private const int NonceBitLength = 192; + private const int NonceByteLength = NonceBitLength / 8; + + private Stream _stream; + private PreSharedKey _key; + private IStreamCipher _readCipher; + private IStreamCipher _writeCipher; + + /// + /// Creates a new instance of the class. + /// + /// + /// The source/destination of the unprotected stream. + /// + /// + /// The pre-shared 256-bit key for the private network of peers. + /// + public Psk1Stream(Stream stream, + PreSharedKey key) + { + if (key.Length != KeyBitLength) + throw new Exception($"The pre-shared key must be {KeyBitLength} bits in length."); + + this._stream = stream; + this._key = key; + } + + private IStreamCipher WriteCipher + { + get + { + if (_writeCipher == null) + { + // Get a random nonce + var nonce = new byte[NonceByteLength]; + using (var rng = RandomNumberGenerator.Create()) + { + rng.GetBytes(nonce); + } + + // Send the nonce to the remote + _stream.Write(nonce, 0, nonce.Length); + + // Create the cipher + _writeCipher = new XSalsa20Engine(); + _writeCipher.Init(true, new ParametersWithIV(new KeyParameter(_key.Value), nonce)); + } + + return _writeCipher; + } + } + + private IStreamCipher ReadCipher + { + get + { + if (_readCipher == null) + { + // Get the nonce from the remote. + var nonce = new byte[NonceByteLength]; + for (int i = 0, n; i < NonceByteLength; i += n) + { + n = _stream.Read(nonce, i, NonceByteLength - i); + if (n < 1) + throw new EndOfStreamException(); + } + + // Create the cipher + _readCipher = new XSalsa20Engine(); + _readCipher.Init(false, new ParametersWithIV(new KeyParameter(_key.Value), nonce)); + } + + return _readCipher; + } + } + + /// + public override bool CanRead => _stream.CanRead; + + /// + public override bool CanSeek => false; + + /// + public override bool CanWrite => _stream.CanWrite; + + /// + public override bool CanTimeout => _stream.CanTimeout; + + /// + public override long Length => throw new NotSupportedException(); + + /// + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + /// + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + + /// + public override void SetLength(long value) { throw new NotSupportedException(); } + + /// + public override int Read(byte[] buffer, int offset, int count) + { + var cipher = ReadCipher; + var n = _stream.Read(buffer, offset, count); + cipher.ProcessBytes(buffer, offset, n, buffer, offset); + return n; + } + + /// + public override async Task ReadAsync(byte[] buffer, + int offset, + int count, + CancellationToken cancellationToken) + { + var cipher = ReadCipher; + var n = await _stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + cipher.ProcessBytes(buffer, offset, n, buffer, offset); + return n; + } + + /// + public override void Flush() { _stream.Flush(); } + + /// + public override Task FlushAsync(CancellationToken cancel) { return _stream.FlushAsync(cancel); } + + /// + public override void Write(byte[] buffer, int offset, int count) + { + var x = new byte[count]; + WriteCipher.ProcessBytes(buffer, offset, count, x, 0); + _stream.Write(x, 0, count); + } + + /// + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + var x = new byte[count]; + WriteCipher.ProcessBytes(buffer, offset, count, x, 0); + return _stream.WriteAsync(x, 0, count, cancellationToken); + } + + /// + public override void WriteByte(byte value) { _stream.WriteByte(WriteCipher.ReturnByte(value)); } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) _stream.Dispose(); + base.Dispose(disposing); + } + } +} diff --git a/src/Lib.P2P/SecureCommunication/Secio1.cs b/src/Lib.P2P/SecureCommunication/Secio1.cs new file mode 100644 index 0000000000..cc2d7d7a67 --- /dev/null +++ b/src/Lib.P2P/SecureCommunication/Secio1.cs @@ -0,0 +1,246 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using Lib.P2P.Cryptography; +using Lib.P2P.Protocols; +using MultiFormats; +using Org.BouncyCastle.Security; +using ProtoBuf; +using Semver; + +namespace Lib.P2P.SecureCommunication +{ + /// + /// Creates a secure connection with a peer. + /// + public class Secio1 : IEncryptionProtocol + { + private static ILog _log = LogManager.GetLogger(typeof(Secio1)); + + /// + public string Name { get; } = "secio"; + + /// + public SemVersion Version { get; } = new(1); + + /// + public override string ToString() { return $"/{Name}/{Version}"; } + + /// + public async Task ProcessMessageAsync(PeerConnection connection, + Stream stream, + CancellationToken cancel = default) + { + await EncryptAsync(connection, cancel).ConfigureAwait(false); + } + +#pragma warning disable VSTHRD103 + /// + public async Task EncryptAsync(PeerConnection connection, + CancellationToken cancel = default) + { + var stream = connection.Stream; + var localPeer = connection.LocalPeer; + connection.RemotePeer = connection.RemotePeer ?? new Peer(); + var remotePeer = connection.RemotePeer; + + // ============================================================================= + // step 1. Propose -- propose cipher suite + send pubkey + nonce + SecureRandom rng = new(); + var localNonce = new byte[16]; + rng.NextBytes(localNonce); + var localProposal = new Secio1Propose + { + Nonce = localNonce, + Exchanges = "P-256,P-384,P-521", + Ciphers = "AES-256,AES-128", + Hashes = "SHA256,SHA512", + PublicKey = Convert.FromBase64String(localPeer.PublicKey) + }; + + Serializer.SerializeWithLengthPrefix(stream, localProposal, PrefixStyle.Fixed32BigEndian); + await stream.FlushAsync(cancel).ConfigureAwait(false); + + // ============================================================================= + // step 1.1 Identify -- get identity from their key + var remoteProposal = + Serializer.DeserializeWithLengthPrefix(stream, PrefixStyle.Fixed32BigEndian); + var ridAlg = remoteProposal.PublicKey.Length <= 48 ? "identity" : "sha2-256"; + var remoteId = MultiHash.ComputeHash(remoteProposal.PublicKey, ridAlg); + if (remotePeer.Id == null) + { + remotePeer.Id = remoteId; + } + + else if (remoteId != remotePeer.Id) + { + throw new Exception($"Expected peer '{remotePeer.Id}', got '{remoteId}'"); + } + + // ============================================================================= + // step 1.2 Selection -- select/agree on best encryption parameters + // to determine order, use cmp(H(remote_pubkey||local_rand), H(local_pubkey||remote_rand)). + // oh1 := hashSha256(append(proposeIn.GetPubkey(), nonceOut...)) + // oh2 := hashSha256(append(myPubKeyBytes, proposeIn.GetRand()...)) + // order := bytes.Compare(oh1, oh2) + byte[] oh1; + byte[] oh2; + using (var hasher = MultiHash.GetHashAlgorithm()) + await using (MemoryStream ms = new()) + { + ms.Write(remoteProposal.PublicKey, 0, remoteProposal.PublicKey.Length); + ms.Write(localProposal.Nonce, 0, localProposal.Nonce.Length); + ms.Position = 0; + oh1 = hasher.ComputeHash(ms); + } + + using (var hasher = MultiHash.GetHashAlgorithm()) + await using (MemoryStream ms = new()) + { + ms.Write(localProposal.PublicKey, 0, localProposal.PublicKey.Length); + ms.Write(remoteProposal.Nonce, 0, remoteProposal.Nonce.Length); + ms.Position = 0; + oh2 = hasher.ComputeHash(ms); + } + + var order = 0; + for (var i = 0; order == 0 && i < oh1.Length; ++i) order = oh1[i].CompareTo(oh2[i]); + if (order == 0) + { + throw new Exception("Same keys and nonces; talking to self"); + } + + var curveName = SelectBest(order, localProposal.Exchanges, remoteProposal.Exchanges); + if (curveName == null) + { + throw new Exception("Cannot agree on a key exchange."); + } + + var cipherName = SelectBest(order, localProposal.Ciphers, remoteProposal.Ciphers); + if (cipherName == null) + { + throw new Exception("Cannot agree on a chipher."); + } + + var hashName = SelectBest(order, localProposal.Hashes, remoteProposal.Hashes); + if (hashName == null) + { + throw new Exception("Cannot agree on a hash."); + } + + // ============================================================================= + // step 2. Exchange -- exchange (signed) ephemeral keys. verify signatures. + + // Generate EphemeralPubKey + var localEphemeralKey = EphermalKey.Generate(curveName); + var localEphemeralPublicKey = localEphemeralKey.PublicKeyBytes(); + + // Send Exchange packet + Secio1Exchange localExchange = new(); + await using (MemoryStream ms = new()) + { + Serializer.Serialize(ms, localProposal); + Serializer.Serialize(ms, remoteProposal); + ms.Write(localEphemeralPublicKey, 0, localEphemeralPublicKey.Length); + localExchange.Signature = connection.LocalPeerKey.Sign(ms.ToArray()); + } + + localExchange.EPublicKey = localEphemeralPublicKey; + Serializer.SerializeWithLengthPrefix(stream, localExchange, PrefixStyle.Fixed32BigEndian); + await stream.FlushAsync(cancel).ConfigureAwait(false); + + // Receive their Exchange packet. If nothing, then most likely the + // remote has closed the connection because it does not like us. + var remoteExchange = + Serializer.DeserializeWithLengthPrefix(stream, PrefixStyle.Fixed32BigEndian); + if (remoteExchange == null) + { + throw new Exception("Remote refuses the SECIO exchange."); + } + + // ============================================================================= + // step 2.1. Verify -- verify their exchange packet is good. + var remotePeerKey = Key.CreatePublicKeyFromIpfs(remoteProposal.PublicKey); + await using (MemoryStream ms = new()) + { + Serializer.Serialize(ms, remoteProposal); + Serializer.Serialize(ms, localProposal); + ms.Write(remoteExchange.EPublicKey, 0, remoteExchange.EPublicKey.Length); + remotePeerKey.Verify(ms.ToArray(), remoteExchange.Signature); + } + + var remoteEphemeralKey = EphermalKey.CreatePublicKeyFromDfs(curveName, remoteExchange.EPublicKey); + + // ============================================================================= + // step 2.2. Keys -- generate keys for mac + encryption + var sharedSecret = localEphemeralKey.GenerateSharedSecret(remoteEphemeralKey); + StretchedKey.Generate(cipherName, hashName, sharedSecret, out var k1, out var k2); + if (order < 0) + { + var tmp = k1; + k1 = k2; + k2 = tmp; + } + + // ============================================================================= + // step 2.3. MAC + Cipher -- prepare MAC + cipher + Secio1Stream secureStream = new(stream, cipherName, hashName, k1, k2); + + // ============================================================================= + // step 3. Finish -- send expected message to verify encryption works (send local nonce) + + // Send thier nonce, + await secureStream.WriteAsync(remoteProposal.Nonce, 0, remoteProposal.Nonce.Length, cancel) + .ConfigureAwait(false); + await secureStream.FlushAsync(cancel).ConfigureAwait(false); + + // Receive our nonce. + var verification = new byte[localNonce.Length]; + await secureStream.ReadExactAsync(verification, 0, verification.Length, cancel); + if (!localNonce.SequenceEqual(verification)) throw new Exception($"SECIO verification message failure."); + + _log.Debug($"Secure session with {remotePeer}"); + + // Fill in the remote peer + remotePeer.PublicKey = Convert.ToBase64String(remoteProposal.PublicKey); + + // Set secure task done + connection.Stream = secureStream; + connection.SecurityEstablished.SetResult(true); + return secureStream; + } + + private string SelectBest(int order, string local, string remote) + { + var first = order < 0 ? remote.Split(',') : local.Split(','); + var second = order < 0 ? local.Split(',') : remote.Split(','); + return first.FirstOrDefault(f => second.Contains(f)); + } + } +} diff --git a/src/Lib.P2P/SecureCommunication/Secio1Messages.cs b/src/Lib.P2P/SecureCommunication/Secio1Messages.cs new file mode 100644 index 0000000000..df2f0a348e --- /dev/null +++ b/src/Lib.P2P/SecureCommunication/Secio1Messages.cs @@ -0,0 +1,73 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using ProtoBuf; + +/* From https://github.com/libp2p/js-libp2p-secio/blob/master/src/handshake/secio.proto.js + +module.exports = `message Propose +{ + optional bytes rand = 1; + optional bytes pubkey = 2; + optional string exchanges = 3; + optional string ciphers = 4; + optional string hashes = 5; +} +message Exchange +{ + optional bytes epubkey = 1; + optional bytes signature = 2; +} +*/ + +namespace Lib.P2P.SecureCommunication +{ + [ProtoContract] + internal sealed class Secio1Propose + { + [ProtoMember(1)] + public byte[] Nonce; + + [ProtoMember(2)] + public byte[] PublicKey; + + [ProtoMember(3)] + public string Exchanges; + + [ProtoMember(4)] + public string Ciphers; + + [ProtoMember(5)] + public string Hashes; + } + + [ProtoContract] + internal sealed class Secio1Exchange + { + [ProtoMember(1)] + public byte[] EPublicKey; + + [ProtoMember(2)] + public byte[] Signature; + } +} diff --git a/src/Lib.P2P/SecureCommunication/Secio1Stream.cs b/src/Lib.P2P/SecureCommunication/Secio1Stream.cs new file mode 100644 index 0000000000..3be5f89405 --- /dev/null +++ b/src/Lib.P2P/SecureCommunication/Secio1Stream.cs @@ -0,0 +1,270 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Cryptography; +using Org.BouncyCastle.Crypto; +using Org.BouncyCastle.Crypto.Engines; +using Org.BouncyCastle.Crypto.Macs; +using Org.BouncyCastle.Crypto.Parameters; +using Org.BouncyCastle.Security; + +namespace Lib.P2P.SecureCommunication +{ + /// + /// A duplex stream that is encrypted and signed. + /// + /// + /// A packet consists of a [uint32 length of packet | encrypted body | hmac signature of encrypted body]. + /// + /// Writing data is buffered until is + /// called. + /// + /// + public class Secio1Stream : Stream + { + private Stream _stream; + private byte[] _inBlock; + private int _inBlockOffset; + private MemoryStream _outStream = new(); + private HMac _inHmac; + private HMac _outHmac; + private IStreamCipher _decrypt; + private IStreamCipher _encrypt; + + /// + /// Creates a new instance of the class. + /// + /// + /// The source/destination of SECIO packets. + /// + /// + /// The cipher for the , such as AES-256 or AES-128. + /// + /// + /// The hash for the , such as SHA256. + /// + /// + /// The keys used by the local endpoint. + /// + /// + /// The keys used by the remote endpoint. + /// + public Secio1Stream(Stream stream, + string cipherName, + string hashName, + StretchedKey localKey, + StretchedKey remoteKey) + { + this._stream = stream; + + _inHmac = new HMac(DigestUtilities.GetDigest(hashName)); + _inHmac.Init(new KeyParameter(localKey.MacKey)); + + _outHmac = new HMac(DigestUtilities.GetDigest(hashName)); + _outHmac.Init(new KeyParameter(remoteKey.MacKey)); + + if (cipherName == "AES-256" || cipherName == "AES-512") + { + _decrypt = new CtrStreamCipher(new AesEngine()); + ParametersWithIV p = new(new KeyParameter(remoteKey.CipherKey), remoteKey.Iv); + _decrypt.Init(false, p); + + _encrypt = new CtrStreamCipher(new AesEngine()); + p = new ParametersWithIV(new KeyParameter(localKey.CipherKey), localKey.Iv); + _encrypt.Init(true, p); + } + else + { + throw new NotSupportedException($"Cipher '{cipherName}' is not supported."); + } + } + + /// + public override bool CanRead => _stream.CanRead; + + /// + public override bool CanSeek => false; + + /// + public override bool CanWrite => _stream.CanWrite; + + /// + public override bool CanTimeout => false; + + /// + public override long Length => throw new NotSupportedException(); + + /// + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + /// + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + + /// + public override void SetLength(long value) { throw new NotSupportedException(); } + + /// + public override int Read(byte[] buffer, int offset, int count) + { +#pragma warning disable VSTHRD002 + return ReadAsync(buffer, offset, count).GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 + } + + /// + public override async Task ReadAsync(byte[] buffer, + int offset, + int count, + CancellationToken cancellationToken) + { + var total = 0; + while (count > 0) + + // Does the current packet have some unread data? + if (_inBlock != null && _inBlockOffset < _inBlock.Length) + { + var n = Math.Min(_inBlock.Length - _inBlockOffset, count); + Array.Copy(_inBlock, _inBlockOffset, buffer, offset, n); + total += n; + count -= n; + offset += n; + _inBlockOffset += n; + } + + // Otherwise, wait for a new block of data. + else + { + _inBlock = await ReadPacketAsync(cancellationToken); + _inBlockOffset = 0; + } + + return total; + } + + /// + /// Read an encrypted and signed packet. + /// + /// + /// The plain text as an array of bytes. + /// + /// + /// A packet consists of a [uint32 length of packet | encrypted body | hmac signature of encrypted body]. + /// + private async Task ReadPacketAsync(CancellationToken cancel) + { + var lengthBuffer = await ReadPacketBytesAsync(4, cancel).ConfigureAwait(false); + var length = + (lengthBuffer[0] << 24) | + (lengthBuffer[1] << 16) | + (lengthBuffer[2] << 8) | + lengthBuffer[3]; + if (length <= _outHmac.GetMacSize()) + throw new InvalidDataException($"Invalid secio packet length of {length}."); + + var encryptedData = await ReadPacketBytesAsync(length - _outHmac.GetMacSize(), cancel).ConfigureAwait(false); + var signature = await ReadPacketBytesAsync(_outHmac.GetMacSize(), cancel).ConfigureAwait(false); + + var hmac = _outHmac; + var mac = new byte[hmac.GetMacSize()]; + hmac.Reset(); + hmac.BlockUpdate(encryptedData, 0, encryptedData.Length); + hmac.DoFinal(mac, 0); + if (!signature.SequenceEqual(mac)) + throw new InvalidDataException("HMac error"); + + // Decrypt the data in-place. + _decrypt.ProcessBytes(encryptedData, 0, encryptedData.Length, encryptedData, 0); + return encryptedData; + } + + private async Task ReadPacketBytesAsync(int count, CancellationToken cancel) + { + var buffer = new byte[count]; + await _stream.ReadExactAsync(buffer, 0, count, cancel).ConfigureAwait(false); + return buffer; + } + + /// + public override void Flush() + { +#pragma warning disable VSTHRD002 + FlushAsync().GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 + } + + /// + public override async Task FlushAsync(CancellationToken cancel) + { + if (_outStream.Length == 0) + return; + + var data = _outStream.ToArray(); // plain text + _encrypt.ProcessBytes(data, 0, data.Length, data, 0); + + var hmac = _inHmac; + var mac = new byte[hmac.GetMacSize()]; + hmac.Reset(); + hmac.BlockUpdate(data, 0, data.Length); + hmac.DoFinal(mac, 0); + + var length = data.Length + mac.Length; + _stream.WriteByte((byte) (length >> 24)); + _stream.WriteByte((byte) (length >> 16)); + _stream.WriteByte((byte) (length >> 8)); + _stream.WriteByte((byte) length); + await _stream.WriteAsync(data, 0, data.Length, cancel); + await _stream.WriteAsync(mac, 0, mac.Length, cancel); + await _stream.FlushAsync(cancel).ConfigureAwait(false); + + _outStream.SetLength(0); + } + + /// + public override void Write(byte[] buffer, int offset, int count) { _outStream.Write(buffer, offset, count); } + + /// + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + return _outStream.WriteAsync(buffer, offset, count, cancellationToken); + } + + /// + public override void WriteByte(byte value) { _outStream.WriteByte(value); } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) _stream.Dispose(); + base.Dispose(disposing); + } + } +} diff --git a/src/Lib.P2P/StatsStream.cs b/src/Lib.P2P/StatsStream.cs new file mode 100644 index 0000000000..fe999f0594 --- /dev/null +++ b/src/Lib.P2P/StatsStream.cs @@ -0,0 +1,252 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Lib.P2P.Transports; + +namespace Lib.P2P +{ + /// + /// A simple wrapper around another stream that records statistics. + /// + public sealed class StatsStream : Stream + { + /// + /// A summary of all StatStreams. + /// + public static readonly BandwidthData AllBandwidth = new BandwidthData + { + RateIn = 5 * 1024, + RateOut = 1024 + }; + + private Stream _stream; + private long _bytesRead; + private long _bytesWritten; + private DateTime _lastUsed; + + static StatsStream() + { + _ = Task.Run(async () => + { + while (true) + { + await Task.Delay(1000).ConfigureAwait(false); + lock (AllBandwidth) + { + AllBandwidth.RateIn = 0; + AllBandwidth.RateOut = 0; + } + } + }); + } + + /// + /// Create a for the specified stream. + /// + internal StatsStream(Stream stream) { this._stream = stream; } + + /// + /// Total number of bytes read on the stream. + /// + internal long BytesRead => _bytesRead; + + /// + /// Total number of byte written to the stream. + /// + internal long BytesWritten => _bytesWritten; + + /// + /// The last time a write or read occured. + /// + internal DateTime LastUsed => _lastUsed; + + /// + public override bool CanRead => _stream.CanRead; + + /// + public override bool CanSeek => _stream.CanSeek; + + /// + public override bool CanWrite => _stream.CanWrite; + + /// + public override long Length => _stream.Length; + + /// + public override bool CanTimeout => _stream.CanTimeout; + + /// + public override int ReadTimeout { get => _stream.ReadTimeout; set => _stream.ReadTimeout = value; } + + /// + public override long Position { get => _stream.Position; set => _stream.Position = value; } + + /// + public override int WriteTimeout { get => _stream.WriteTimeout; set => _stream.WriteTimeout = value; } + + /// + public override void Flush() { _stream.Flush(); } + + /// + public override int Read(byte[] buffer, int offset, int count) + { + var n = _stream.Read(buffer, offset, count); + _bytesRead += n; + _lastUsed = DateTime.Now; + if (n <= 0) + { + return n; + } + + AllBandwidth.TotalIn += (ulong) n; + AllBandwidth.RateIn += n; + + return n; + } + + /// + public override long Seek(long offset, SeekOrigin origin) { return _stream.Seek(offset, origin); } + + /// + public override void SetLength(long value) { _stream.SetLength(value); } + + /// + public override void Write(byte[] buffer, int offset, int count) + { + _stream.Write(buffer, offset, count); + _bytesWritten += count; + _lastUsed = DateTime.Now; + + if (count <= 0) + { + return; + } + + AllBandwidth.TotalOut += (ulong) count; + AllBandwidth.RateOut += count; + } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + _stream.Dispose(); + } + + base.Dispose(disposing); + } + + /// + public override Task FlushAsync(CancellationToken cancellationToken) + { + return _stream.FlushAsync(cancellationToken); + } + + /// + public override async Task ReadAsync(byte[] buffer, + int offset, + int count, + CancellationToken cancellationToken) + { + try + { + var n = await _stream.ReadAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + _bytesRead += n; + _lastUsed = DateTime.Now; + if (n <= 0) + { + return n; + } + + AllBandwidth.TotalIn += (ulong) n; + AllBandwidth.RateIn += n; + + return n; + } + catch (Exception) when (cancellationToken.IsCancellationRequested) + { + return 0; + } + } + + /// + public override async Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + try + { + await _stream.WriteAsync(buffer, offset, count, cancellationToken).ConfigureAwait(false); + _bytesWritten += count; + _lastUsed = DateTime.Now; + if (count > 0) + + //lock (AllBandwidth) + { + AllBandwidth.TotalOut += (ulong) count; + AllBandwidth.RateOut += count; + } + } + catch (Exception) when (cancellationToken.IsCancellationRequested) + { + // eat it. + } + } + + /// + public override int ReadByte() + { + var n = _stream.ReadByte(); + if (n > -1) + { + ++_bytesRead; + + //lock (AllBandwidth) + { + ++AllBandwidth.TotalIn; + ++AllBandwidth.RateIn; + } + } + + _lastUsed = DateTime.Now; + return n; + } + + /// + public override void WriteByte(byte value) + { + _stream.WriteByte(value); + ++_bytesWritten; + _lastUsed = DateTime.Now; + + //lock (AllBandwidth) + { + ++AllBandwidth.TotalOut; + ++AllBandwidth.RateOut; + } + } + } +} diff --git a/src/Lib.P2P/StreamExtensions.cs b/src/Lib.P2P/StreamExtensions.cs new file mode 100644 index 0000000000..7d353c99cc --- /dev/null +++ b/src/Lib.P2P/StreamExtensions.cs @@ -0,0 +1,112 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Lib.P2P +{ + /// + /// + /// + public static class StreamExtensions + { + /// + /// Asynchronously reads a sequence of bytes from the stream and advances + /// the position within the stream by the number of bytes read. + /// + /// + /// The stream to read from. + /// + /// + /// The buffer to write the data into. + /// + /// + /// The byte offset in at which to begin + /// writing data from the . + /// + /// + /// The number of bytes to read. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// When the does not have + /// bytes. + /// + public static async Task ReadExactAsync(this Stream stream, byte[] buffer, int offset, int length) + { + while (0 < length) + { + var n = await stream.ReadAsync(buffer, offset, length); + if (n == 0) throw new EndOfStreamException(); + offset += n; + length -= n; + } + } + + /// + /// Asynchronously reads a sequence of bytes from the stream and advances + /// the position within the stream by the number of bytes read. + /// + /// + /// The stream to read from. + /// + /// + /// The buffer to write the data into. + /// + /// + /// The byte offset in at which to begin + /// writing data from the . + /// + /// + /// The number of bytes to read. + /// + /// + /// Is used to stop the task. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// When the does not have + /// bytes. + /// + public static async Task ReadExactAsync(this Stream stream, + byte[] buffer, + int offset, + int length, + CancellationToken cancel) + { + while (0 < length) + { + var n = await stream.ReadAsync(buffer, offset, length, cancel); + if (n == 0) throw new EndOfStreamException(); + offset += n; + length -= n; + } + } + } +} diff --git a/src/Lib.P2P/SwarmService.cs b/src/Lib.P2P/SwarmService.cs new file mode 100644 index 0000000000..64bea55f42 --- /dev/null +++ b/src/Lib.P2P/SwarmService.cs @@ -0,0 +1,1149 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net.NetworkInformation; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using Lib.P2P.Cryptography; +using Lib.P2P.Protocols; +using Lib.P2P.Routing; +using Lib.P2P.SecureCommunication; +using Lib.P2P.Transports; +using MultiFormats; +using Nito.AsyncEx; + +namespace Lib.P2P +{ + /// + /// Manages communication with other peers. + /// + public class SwarmService : ISwarmService + { + private static ILog _log = LogManager.GetLogger(typeof(SwarmService)); + + /// + /// The time to wait for a low level connection to be established. + /// + /// + /// Defaults to 30 seconds. + /// + public TimeSpan TransportConnectionTimeout = TimeSpan.FromSeconds(30); + + /// + /// The supported protocols. + /// + /// + /// Use sychronized access, e.g. lock (protocols) { ... }. + /// + private List _protocols = new List + { + new Multistream1(), + new Secio1(), + new Identify1(), + new Mplex67() + }; + + /// + /// Added to connection protocols when needed. + /// + private readonly Plaintext1 _plaintext1 = new(); + + private Peer _localPeer; + + /// + /// Raised when a listener is establihed. + /// + /// + /// Raised when + /// succeeds. + /// + public event EventHandler ListenerEstablished; + + /// + /// Raised when a connection to another peer is established. + /// + public event EventHandler ConnectionEstablished; + + /// + /// Raised when a new peer is discovered for the first time. + /// + public event EventHandler PeerDiscovered; + + /// + /// Raised when a peer's connection is closed. + /// + public event EventHandler PeerDisconnected; + + /// + /// Raised when a peer should no longer be used. + /// + /// + /// This event indicates that the peer has been removed + /// from the and should no longer + /// be used. + /// + public event EventHandler PeerRemoved; + + /// + /// Raised when a peer cannot be connected to. + /// + public event EventHandler PeerNotReachable; + + /// + /// The local peer. + /// + /// + /// The local peer must have an and + /// . + /// + public Peer LocalPeer + { + get => _localPeer; + set + { + if (value == null) + { + throw new ArgumentNullException(); + } + + if (value.Id == null) + { + throw new ArgumentNullException($"peer.id"); + } + + if (value.PublicKey == null) + { + throw new ArgumentNullException($"peer.PublicKey"); + } + + if (!value.IsValid()) + { + throw new ArgumentException("Invalid peer."); + } + + _localPeer = value; + } + } + + /// + /// The private key of the local peer. + /// + /// + /// Used to prove the identity of the . + /// + public Key LocalPeerKey { get; set; } + + /// + /// Other nodes. Key is the bae58 hash of the peer ID. + /// + private readonly ConcurrentDictionary _otherPeers = new(); + + /// + /// Used to cancel any task when the swarm is stopped. + /// + private CancellationTokenSource _swarmCancellation; + + /// + /// Outstanding connection tasks initiated by the local peer. + /// + private readonly ConcurrentDictionary> _pendingConnections = + new(); + + /// + /// Outstanding connection tasks initiated by a remote peer. + /// + private readonly ConcurrentDictionary _pendingRemoteConnections = + new(); + + /// + /// Manages the swarm's peer connections. + /// + public ConnectionManager Manager { private set; get; } = new ConnectionManager(); + + /// + /// Use to find addresses of a peer. + /// + public IPeerRouting Router { get; set; } + + /// + /// Provides access to a private network of peers. + /// + public INetworkProtector NetworkProtector { get; set; } + + /// + /// Determines if the swarm has been started. + /// + /// + /// true if the swarm has started; otherwise, false. + /// + /// + /// + public bool IsRunning { get; private set; } + + /// + /// Cancellation tokens for the listeners. + /// + private ConcurrentDictionary _listeners = + new(); + + /// + /// Get the sequence of all known peer addresses. + /// + /// + /// Contains any peer address that has been + /// discovered. + /// + /// + public IEnumerable KnownPeerAddresses + { + get + { + return _otherPeers + .Values + .SelectMany(p => p.Addresses); + } + } + + /// + /// Get the sequence of all known peers. + /// + /// + /// Contains any peer that has been + /// discovered. + /// + /// + public IEnumerable KnownPeers => _otherPeers.Values; + + /// + /// Register that a peer's address has been discovered. + /// + /// + /// An address to the peer. It must end with the peer ID. + /// + /// + /// The that is registered. + /// + /// + /// The or policies forbid it. + /// Or the "p2p/ipfs" protocol name is missing. + /// + /// + /// If the is not already known, then it is + /// added to the . + /// + /// + public Peer RegisterPeerAddress(MultiAddress address) + { + var peer = new Peer + { + Id = address.PeerId, + Addresses = new List { address } + }; + + return RegisterPeer(peer); + } + + /// + /// Register that a peer has been discovered. + /// + /// + /// The newly discovered peer. + /// + /// + /// The registered peer. + /// + /// + /// If the peer already exists, then the existing peer is updated with supplied + /// information and is then returned. Otherwise, the + /// is added to known peers and is returned. + /// + /// If the peer already exists, then a union of the existing and new addresses + /// is used. For all other information the 's information + /// is used if not null. + /// + /// + /// If peer does not already exist, then the event + /// is raised. + /// + /// + /// + /// The or policies forbid it. + /// + public Peer RegisterPeer(Peer peer) + { + return RegisterPeer(peer, false); + } + + /// + public Peer RegisterPeer(Peer peer, bool ignoreRestrictionLists) + { + if (peer.Id == null) + { + throw new ArgumentNullException(nameof(peer)); + } + + if (peer.Id == LocalPeer.Id) + { + throw new ArgumentException("Cannot register self."); + } + + if (!IsAllowed(peer) && !ignoreRestrictionLists) + { + throw new Exception($"Communication with '{peer}' is not allowed."); + } + + bool isNew = false; + var p = _otherPeers.AddOrUpdate(peer.Id.ToBase58(), + id => + { + isNew = true; + return peer; + }, + (id, existing) => + { + if (ReferenceEquals(existing, peer)) + { + return existing; + } + + existing.AgentVersion = peer.AgentVersion ?? existing.AgentVersion; + existing.ProtocolVersion = peer.ProtocolVersion ?? existing.ProtocolVersion; + existing.PublicKey = peer.PublicKey ?? existing.PublicKey; + existing.Latency = peer.Latency ?? existing.Latency; + existing.Addresses = existing + .Addresses + .Union(peer.Addresses) + .ToList(); + + return existing; + }); + + if (!isNew) + { + return p; + } + + if (_log.IsDebugEnabled) + { + _log.Debug($"New peer registerd {p}"); + } + + PeerDiscovered?.Invoke(this, p); + + return p; + } + + /// + /// Deregister a peer. + /// + /// + /// The peer to remove.. + /// + /// + /// Remove all knowledge of the peer. The event + /// is raised. + /// + public void DeregisterPeer(Peer peer) + { + if (peer.Id == null) + { + throw new ArgumentNullException(nameof(peer)); + } + + if (_otherPeers.TryRemove(peer.Id.ToBase58(), out var found)) + { + peer = found; + } + + PeerRemoved?.Invoke(this, peer); + } + + /// + /// Determines if a connection is being made to the peer. + /// + /// + /// A . + /// + /// + /// true is the has a pending connection. + /// + public bool HasPendingConnection(Peer peer) { return _pendingConnections.TryGetValue(peer, out _); } + + /// + /// The addresses that cannot be used. + /// + public MultiAddressBlackList BlackList { get; set; } = new(); + + /// + /// The addresses that can be used. + /// + public MultiAddressWhiteList WhiteList { get; set; } = new(); + + /// + /// + /// + /// + public SwarmService(Peer localPeer, IPeerRouting dhtService = null) + { + if (localPeer != null) + { + LocalPeer = localPeer; + } + + Router = dhtService; + } + + /// + public Task StartAsync() + { + if (LocalPeer == null) + { + throw new NotSupportedException("The LocalPeer is not defined."); + } + + // Many of the unit tests do not setup the LocalPeerKey. If + // its missing, then just use plaintext connection. + // TODO: make the tests setup the security protocols. + if (LocalPeerKey == null) + { + lock (_protocols) + { + var security = _protocols.OfType().ToArray(); + foreach (var p in security) + { + _protocols.Remove(p); + } + + _protocols.Add(_plaintext1); + } + + _log.Warn("Peer key is missing, using unencrypted connections."); + } + + Manager.PeerDisconnected += OnPeerDisconnected; + IsRunning = true; + _swarmCancellation = new CancellationTokenSource(); + _log.Debug("Started"); + + return Task.CompletedTask; + } + + private void OnPeerDisconnected(object sender, MultiHash peerId) + { + if (!_otherPeers.TryGetValue(peerId.ToBase58(), out var peer)) + { + peer = new Peer { Id = peerId }; + } + + PeerDisconnected?.Invoke(this, peer); + } + + /// + public async Task StopAsync() + { + IsRunning = false; + _swarmCancellation?.Cancel(true); + + _log.Debug($"Stopping {LocalPeer}"); + + // Stop the listeners. + while (_listeners.Count > 0) + { + await StopListeningAsync(_listeners.Keys.First()).ConfigureAwait(false); + } + + // Disconnect from remote peers. + Manager.Clear(); + Manager.PeerDisconnected -= OnPeerDisconnected; + + _otherPeers.Clear(); + _listeners.Clear(); + _pendingConnections.Clear(); + _pendingRemoteConnections.Clear(); + BlackList = new MultiAddressBlackList(); + WhiteList = new MultiAddressWhiteList(); + + _log.Debug($"Stopped {LocalPeer}"); + } + + /// + /// Connect to a peer using the specified . + /// + /// + /// An ipfs , such as + /// /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the . + /// + /// + /// If already connected to the peer and is active on any address, then + /// the existing connection is returned. + /// + public async Task ConnectAsync(MultiAddress address, + CancellationToken cancel = default) + { + var peer = RegisterPeerAddress(address); + return await ConnectAsync(peer, cancel).ConfigureAwait(false); + } + + /// + /// Connect to a peer. + /// + /// + /// A peer to connect to. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the . + /// + /// + /// If already connected to the peer and is active on any address, then + /// the existing connection is returned. + /// + public async Task ConnectAsync(Peer peer, CancellationToken cancel = default) + { + if (!IsRunning) + { + throw new Exception("The swarm is not running."); + } + + peer = RegisterPeer(peer); + + // If connected and still open, then use the existing connection. + if (Manager.TryGet(peer, out var conn)) + { + return conn; + } + + // Use a current connection attempt to the peer or create a new one. + try + { + using (var cts = CancellationTokenSource.CreateLinkedTokenSource(_swarmCancellation.Token, cancel)) + { + return await _pendingConnections + .GetOrAdd(peer, + key => new AsyncLazy(() => DialAsync(peer, peer.Addresses, cts.Token))) + .ConfigureAwait(false); + } + } + catch (Exception) + { + PeerNotReachable?.Invoke(this, peer); + throw; + } + finally + { + _pendingConnections.TryRemove(peer, out _); + } + } + + /// + /// Create a stream to the peer that talks the specified protocol. + /// + /// + /// The remote peer. + /// + /// + /// The protocol name, such as "/foo/0.42.0". + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the new to the . + /// + /// + /// + /// When finished, the caller must the + /// new stream. + /// + /// + public async Task DialAsync(Peer peer, + string protocol, + CancellationToken cancel = default) + { + peer = RegisterPeer(peer); + + // Get a connection and then a muxer to the peer. + var connection = await ConnectAsync(peer, cancel).ConfigureAwait(false); + var muxer = await connection.MuxerEstablished.Task.ConfigureAwait(false); + + // Create a new stream for the peer protocol. + var stream = await muxer.CreateStreamAsync(protocol, cancel).ConfigureAwait(false); + try + { + await connection.EstablishProtocolAsync("/multistream/", stream, cancel).ConfigureAwait(false); + + await Message.WriteAsync(protocol, stream, cancel).ConfigureAwait(false); + var result = await Message.ReadStringAsync(stream, cancel).ConfigureAwait(false); + if (result != protocol) + { + throw new Exception($"Protocol '{protocol}' not supported by '{peer}'."); + } + + return stream; + } + catch (Exception) + { + stream?.DisposeAsync(); + throw; + } + } + + /// + /// Establish a duplex stream between the local and remote peer. + /// + /// + /// + /// + /// + private async Task DialAsync(Peer remote, + IEnumerable addrs, + CancellationToken cancel) + { + _log.Debug($"Dialing {remote}"); + + if (remote == LocalPeer) throw new Exception("Cannot dial self."); + + // If no addresses, then ask peer routing. + var multiAddresses = addrs.ToList(); + if (Router != null && !multiAddresses.Any()) + { + var found = await Router.FindPeerAsync(remote.Id, cancel).ConfigureAwait(false); + addrs = found.Addresses; + remote.Addresses = addrs; + } + + // Get the addresses we can use to dial the remote. Filter + // out any addresses (ip and port) we are listening on. + var blackList = _listeners.Keys + .Select(a => a.WithoutPeerId()) + .ToArray(); + + var possibleAddresses = + (await Task.WhenAll(multiAddresses.Select(a => a.ResolveAsync(cancel))).ConfigureAwait(false)) + .SelectMany(a => a) + .Where(a => !blackList.Contains(a.WithoutPeerId())) + .Select(a => a.WithPeerId(remote.Id)) + .Distinct() + .ToArray(); + + if (possibleAddresses.Length == 0) + { + throw new Exception($"{remote} has no known or reachable address."); + } + + // Try the various addresses in parallel. The first one to complete wins. + PeerConnection connection = null; + try + { + using (var timeout = new CancellationTokenSource(TransportConnectionTimeout)) + { + using (var cts = CancellationTokenSource.CreateLinkedTokenSource(timeout.Token, cancel)) + { + var attempts = possibleAddresses + .Select(a => DialAsync(remote, a, cts.Token)); + connection = await TaskHelper.WhenAnyResultAsync(attempts, cts.Token).ConfigureAwait(false); + cts.Cancel(); // stop other dialing tasks. + } + } + } + catch (Exception e) + { + var attemped = string.Join(", ", possibleAddresses.Select(a => a.ToString())); + _log.Trace($"Cannot dial {attemped}"); + throw new Exception($"Cannot dial {remote}.", e); + } + + // Do the connection handshake. + try + { + MountProtocols(connection); + IEncryptionProtocol[] security; + lock (_protocols) + { + security = _protocols.OfType().ToArray(); + } + + await connection.InitiateAsync(security, cancel).ConfigureAwait(false); + await connection.MuxerEstablished.Task.ConfigureAwait(false); + Identify1 identify; + lock (_protocols) + { + identify = _protocols.OfType().First(); + } + + await identify.GetRemotePeerAsync(connection, cancel).ConfigureAwait(false); + } + catch (Exception) + { + connection.Dispose(); + throw; + } + + var actual = Manager.Add(connection); + if (actual == connection) + { + ConnectionEstablished?.Invoke(this, connection); + } + + return actual; + } + + private async Task DialAsync(Peer remote, MultiAddress addr, CancellationToken cancel) + { + // TODO: HACK: Currenty only the ipfs/p2p is supported. + // short circuit to make life faster. + if (addr.Protocols.Count != 3 + || !(addr.Protocols[2].Name == "ipfs" || addr.Protocols[2].Name == "p2p")) + { + throw new Exception($"Cannnot dial; unknown protocol in '{addr}'."); + } + + // Establish the transport stream. + Stream stream = null; + foreach (var protocol in addr.Protocols) + { + cancel.ThrowIfCancellationRequested(); + if (!TransportRegistry.Transports.TryGetValue(protocol.Name, out var transport)) + { + continue; + } + + stream = await transport().ConnectAsync(addr, cancel).ConfigureAwait(false); + if (cancel.IsCancellationRequested) + { + stream?.DisposeAsync(); + continue; + } + + break; + } + + if (stream == null) + { + throw new Exception("Missing a known transport protocol name."); + } + + // Build the connection. + var connection = new PeerConnection + { + IsIncoming = false, + LocalPeer = LocalPeer, + + // TODO: LocalAddress + LocalPeerKey = LocalPeerKey, + RemotePeer = remote, + RemoteAddress = addr, + Stream = stream + }; + + // Are we communicating to a private network? + if (NetworkProtector != null) + { + connection.Stream = await NetworkProtector.ProtectAsync(connection, cancel).ConfigureAwait(false); + } + + return connection; + } + + /// + /// Disconnect from a peer. + /// + /// + /// An ipfs , such as + /// /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// If the peer is not conected, then nothing happens. + /// + public Task DisconnectAsync(MultiAddress address, CancellationToken cancel = default) + { + Manager.Remove(address.PeerId); + return Task.CompletedTask; + } + + /// + /// Start listening on the specified . + /// + /// + /// Typically "/ip4/0.0.0.0/tcp/4001" or "/ip6/::/tcp/4001". + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is a than can be used by another peer + /// to connect to tis peer. + /// + /// + /// Already listening on . + /// + /// + /// is missing a transport protocol (such as tcp or udp). + /// + /// + /// Allows other peers to connect + /// to the . + /// + /// The of the are updated. If the refers to + /// any IP address ("/ip4/0.0.0.0" or "/ip6/::") then all network interfaces addresses + /// are added. If the port is zero (as in "/ip6/::/tcp/0"), then the peer addresses contains the actual port number + /// that was assigned. + /// + /// + public Task StartListeningAsync(MultiAddress address) + { + CancellationTokenSource cancel = new(); + + if (!_listeners.TryAdd(address, cancel)) + { + throw new Exception($"Already listening on '{address}'."); + } + + // Start a listener for the transport + var didSomething = false; + foreach (var protocol in address.Protocols) + if (TransportRegistry.Transports.TryGetValue(protocol.Name, out var transport)) + { + address = transport().Listen(address, OnRemoteConnect, cancel.Token); + _listeners.TryAdd(address, cancel); + didSomething = true; + break; + } + + if (!didSomething) + { + throw new ArgumentException($"Missing a transport protocol name '{address}'.", nameof(address)); + } + + MultiAddress result = new($"{address}/ipfs/{LocalPeer.Id}"); + + // Get the actual IP address(es). + IEnumerable addresses = new List(); + var ips = NetworkInterface.GetAllNetworkInterfaces() + + // It appears that the loopback adapter is not UP on Ubuntu 14.04.5 LTS + .Where(nic => nic.OperationalStatus == OperationalStatus.Up + || nic.NetworkInterfaceType == NetworkInterfaceType.Loopback) + .SelectMany(nic => nic.GetIPProperties().UnicastAddresses); + if (result.ToString().StartsWith("/ip4/0.0.0.0/")) + { + addresses = ips + .Where(ip => ip.Address.AddressFamily == AddressFamily.InterNetwork) + .Select(ip => new MultiAddress(result.ToString().Replace("0.0.0.0", ip.Address.ToString()))) + .ToArray(); + } + else if (result.ToString().StartsWith("/ip6/::/")) + { + addresses = ips + .Where(ip => ip.Address.AddressFamily == AddressFamily.InterNetworkV6) + .Select(ip => { return new MultiAddress(result.ToString().Replace("::", ip.Address.ToString())); }) + .ToArray(); + } + else + { + addresses = new[] { result }; + } + + if (!addresses.Any()) + { + var msg = "Cannot determine address(es) for " + result; + + foreach (var ip in ips) + { + msg += " nic-ip: " + ip.Address; + } + + cancel.Cancel(); + throw new Exception(msg); + } + + // Add actual addresses to listeners and local peer addresses. + foreach (var a in addresses) + { + _log.Debug($"Listening on {a}"); + _listeners.TryAdd(a, cancel); + } + + LocalPeer.Addresses = LocalPeer + .Addresses + .Union(addresses) + .ToArray(); + + ListenerEstablished?.Invoke(this, LocalPeer); + return Task.FromResult(addresses.First()); + } + +#pragma warning disable VSTHRD100 // Avoid async void methods + /// + /// Called when a remote peer is connecting to the local peer. + /// + /// + /// The stream to the remote peer. + /// + /// + /// The local peer's address. + /// + /// + /// The remote peer's address. + /// + /// + /// Establishes the protocols of the connection. Any exception is simply + /// logged as warning. + /// + private async void OnRemoteConnect(Stream stream, MultiAddress local, MultiAddress remote) +#pragma warning restore VSTHRD100 // Avoid async void methods + { + if (!IsRunning) + { + try + { + stream.Dispose(); + } + catch (Exception) + { + // eat it. + } + + return; + } + + // If the remote is already trying to establish a connection, then we + // can just refuse this one. + if (!_pendingRemoteConnections.TryAdd(remote, null)) + { + _log.Debug($"Duplicate remote connection from {remote}"); + try + { + stream.Dispose(); + } + catch (Exception) + { + // eat it. + } + + return; + } + + try + { + if (_log.IsDebugEnabled) + { + _log.Debug($"remote connect from {remote}"); + } + + // TODO: Check the policies + + var connection = new PeerConnection + { + IsIncoming = true, + LocalPeer = LocalPeer, + LocalAddress = local, + LocalPeerKey = LocalPeerKey, + RemoteAddress = remote, + Stream = stream + }; + + // Are we communicating to a private network? + if (NetworkProtector != null) + { + connection.Stream = await NetworkProtector.ProtectAsync(connection).ConfigureAwait(false); + } + + // Mount the protocols. + MountProtocols(connection); + + // Start the handshake + // TODO: Isn't connection cancel token required. + _ = connection.ReadMessagesAsync(default); + + // Wait for security to be established. + await connection.SecurityEstablished.Task.ConfigureAwait(false); + + // TODO: Maybe connection.LocalPeerKey = null; + + // Wait for the handshake to complete. + await connection.MuxerEstablished.Task; + + // Need details on the remote peer. + Identify1 identify; + lock (_protocols) + { + identify = _protocols.OfType().First(); + } + + connection.RemotePeer = await identify.GetRemotePeerAsync(connection, default) + .ConfigureAwait(false); + + connection.RemotePeer = RegisterPeer(connection.RemotePeer, true); + connection.RemoteAddress = new MultiAddress($"{remote}/ipfs/{connection.RemotePeer.Id}"); + var actual = Manager.Add(connection); + if (actual == connection) + { + ConnectionEstablished?.Invoke(this, connection); + } + } + catch (Exception e) + { + if (_log.IsDebugEnabled) + { + _log.Debug($"remote connect from {remote} failed: {e.Message}"); + } + + try + { + stream.Dispose(); + } + catch (Exception) + { + // ignore + } + } + finally + { + _pendingRemoteConnections.TryRemove(remote, out _); + } + } + + /// + /// Add a protocol that is supported by the swarm. + /// + /// + /// The protocol to add. + /// + public void AddProtocol(IPeerProtocol protocol) + { + lock (_protocols) + { + _protocols.Add(protocol); + } + } + + /// + /// Remove a protocol from the swarm. + /// + /// + /// The protocol to remove. + /// + public void RemoveProtocol(IPeerProtocol protocol) + { + lock (_protocols) + { + _protocols.Remove(protocol); + } + } + + private void MountProtocols(PeerConnection connection) + { + lock (_protocols) + { + connection.AddProtocols(_protocols); + } + } + + /// + /// Stop listening on the specified . + /// + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// Allows other peers to connect + /// to the . + /// + /// The addresses of the are updated. + /// + /// + public async Task StopListeningAsync(MultiAddress address) + { + if (!_listeners.TryRemove(address, out var listener)) + { + return; + } + + try + { + if (!listener.IsCancellationRequested) + { + listener.Cancel(false); + } + + // Remove any local peer address that depend on the cancellation token. + var others = _listeners + .Where(l => l.Value == listener) + .Select(l => l.Key) + .ToArray(); + + LocalPeer.Addresses = LocalPeer.Addresses + .Where(a => a != address) + .Where(a => !others.Contains(a)) + .ToArray(); + + foreach (var other in others) + { + _listeners.TryRemove(other, out _); + } + + // Give some time away, so that cancel can run + // TODO: Would be nice to make this deterministic. + await Task.Delay(TimeSpan.FromMilliseconds(100)).ConfigureAwait(false); + } + catch (Exception e) + { + _log.Error("stop listening failed", e); + } + } + + /// + public bool IsAllowed(MultiAddress target) + { + return BlackList.IsAllowed(target) + && WhiteList.IsAllowed(target); + } + + /// + public bool IsAllowed(Peer peer) { return peer.Addresses.All(IsAllowed); } + } +} diff --git a/src/Lib.P2P/TaskHelper.cs b/src/Lib.P2P/TaskHelper.cs new file mode 100644 index 0000000000..ee353d5f87 --- /dev/null +++ b/src/Lib.P2P/TaskHelper.cs @@ -0,0 +1,127 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; + +namespace Lib.P2P +{ + /// + /// Some helpers for tasks. + /// + public static class TaskHelper + { + /// + /// Gets the first result from a set of tasks. + /// + /// + /// The result type of the . + /// + /// + /// The tasks to perform. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// a >. + /// + /// + /// Returns the result of the first task that is not + /// faulted or canceled. + /// + public static async Task WhenAnyResultAsync(IEnumerable> tasks, + CancellationToken cancel) + { + List exceptions = new(); + var running = tasks.ToList(); + while (running.Count > 0) + { + cancel.ThrowIfCancellationRequested(); + var winner = await Task.WhenAny(running).ConfigureAwait(false); + if (!winner.IsCanceled && !winner.IsFaulted) + { + return await winner; + } + + if (winner.IsFaulted) + { + if (winner.Exception is { } ae) + { + exceptions.AddRange(ae.InnerExceptions); + } + else + { + exceptions.Add(winner.Exception); + } + } + + running.Remove(winner); + } + + cancel.ThrowIfCancellationRequested(); + throw new AggregateException("No task(s) returned a result.", exceptions); + } + + /// + /// Run async tasks in parallel, + /// + /// + /// A sequence of some data. + /// + /// + /// The async code to perform. + /// + /// + /// The number of partitions to create. + /// + /// + /// A Task to await. + /// + /// + /// Copied from https://houseofcat.io/tutorials/csharp/async/parallelforeachasync + /// + internal static Task ParallelForEachAsync(this IEnumerable source, Func funcBody, int maxDoP = 4) + { + async Task AwaitPartition(IEnumerator partition) + { + using (partition) + { + while (partition.MoveNext()) await funcBody(partition.Current); + } + } + + return Task.WhenAll( + Partitioner + .Create(source) + .GetPartitions(maxDoP) + .AsParallel() + .Select(AwaitPartition)); + } + } +} diff --git a/src/Lib.P2P/Transports/BandwidthData.cs b/src/Lib.P2P/Transports/BandwidthData.cs new file mode 100644 index 0000000000..dcdc17da14 --- /dev/null +++ b/src/Lib.P2P/Transports/BandwidthData.cs @@ -0,0 +1,54 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace Lib.P2P.Transports +{ + /// + /// The statistics for + /// Catalyst.Ipfs.Core.CoreApi.IStatsApi.BandwidthAsync(System.Threading.CancellationToken) + /// + /// . + /// + public class BandwidthData + { + /// + /// The number of bytes received. + /// + public ulong TotalIn; + + /// + /// The number of bytes sent. + /// + public ulong TotalOut; + + /// + /// TODO + /// + public double RateIn; + + /// + /// TODO + /// + public double RateOut; + } +} diff --git a/src/Lib.P2P/Transports/DatagramStream.cs b/src/Lib.P2P/Transports/DatagramStream.cs new file mode 100644 index 0000000000..8be4420197 --- /dev/null +++ b/src/Lib.P2P/Transports/DatagramStream.cs @@ -0,0 +1,146 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; + +namespace Lib.P2P.Transports +{ + internal class DatagramStream : Stream + { + private Socket _socket; + private bool _ownsSocket; + private MemoryStream _sendBuffer = new(); + private MemoryStream _receiveBuffer = new(); + private byte[] _datagram = new byte[2048]; + + public DatagramStream(Socket socket, bool ownsSocket = false) + { + this._socket = socket; + this._ownsSocket = ownsSocket; + } + + protected override void Dispose(bool disposing) + { + if (disposing) + try + { + Flush(); + } + catch (SocketException) + { + // eat it + } + + if (_ownsSocket && _socket != null) + try + { + _socket.Dispose(); + } + catch (SocketException) + { + // eat it + } + finally + { + _socket = null; + } + + base.Dispose(disposing); + } + + public override bool CanRead => true; + + public override bool CanSeek => false; + + public override bool CanWrite => true; + + public override long Length => throw new NotSupportedException(); + + public override long Position + { + get => throw new NotSupportedException(); + set => throw new NotSupportedException(); + } + + public override void Flush() + { +#pragma warning disable VSTHRD002 + FlushAsync().GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 + } + + public override async Task FlushAsync(CancellationToken cancellationToken) + { + if (_sendBuffer.Position > 0) + { + ArraySegment bytes = new(_sendBuffer.ToArray()); + _sendBuffer.Position = 0; + await _socket.SendAsync(bytes, SocketFlags.None).ConfigureAwait(false); + } + } + + public override int Read(byte[] buffer, int offset, int count) + { +#pragma warning disable VSTHRD002 + return ReadAsync(buffer, offset, count).GetAwaiter().GetResult(); +#pragma warning restore VSTHRD002 + } + + public override async Task ReadAsync(byte[] buffer, + int offset, + int count, + CancellationToken cancellationToken) + { + // If no data. + if (_receiveBuffer.Position != _receiveBuffer.Length) + { + return _receiveBuffer.Read(buffer, offset, count); + } + + await FlushAsync(cancellationToken).ConfigureAwait(false); + _receiveBuffer.Position = 0; + _receiveBuffer.SetLength(0); + var size = _socket.Receive(_datagram); + await _receiveBuffer.WriteAsync(_datagram, 0, size, cancellationToken); + _receiveBuffer.Position = 0; + + return _receiveBuffer.Read(buffer, offset, count); + } + + public override long Seek(long offset, SeekOrigin origin) { throw new NotSupportedException(); } + + public override void SetLength(long value) { throw new NotSupportedException(); } + + public override void Write(byte[] buffer, int offset, int count) { _sendBuffer.Write(buffer, offset, count); } + + public override Task WriteAsync(byte[] buffer, int offset, int count, CancellationToken cancellationToken) + { + Write(buffer, offset, count); + return Task.CompletedTask; + } + } +} diff --git a/src/Lib.P2P/Transports/IPeerTransport.cs b/src/Lib.P2P/Transports/IPeerTransport.cs new file mode 100644 index 0000000000..c0a3f9a685 --- /dev/null +++ b/src/Lib.P2P/Transports/IPeerTransport.cs @@ -0,0 +1,88 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using MultiFormats; + +namespace Lib.P2P.Transports +{ + /// + /// Establishes a duplex stream between two peers + /// over a specific network transport. + /// + public interface IPeerTransport + { + /// + /// Connect to a peer. + /// + /// + /// The address of the peer. + /// + /// + /// Is used to stop the task. When cancelled, a null is returned. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is a duplex or null. + /// + Task ConnectAsync(MultiAddress address, CancellationToken cancel = default); + + /// + /// Listen to any peer connections on the specified address. + /// + /// + /// The address to listen on. + /// + /// + /// The action to perform when a peer connection is received. + /// + /// + /// Is used to stop the connection listener. When cancelled, the + /// is NOT raised. + /// + /// + /// The actual address of the listener. + /// + /// + /// The is invoked on the peer listener thread. If + /// it throws, then the connection is closed but the listener remains + /// active. It is passed a duplex stream, the local address and the remote + /// address. + /// + /// To stop listening, the parameter + /// must be supplied and then use the + /// method. + /// + /// + /// For socket based transports (tcp or upd), if the port is not defined + /// or is zero an ephermal port is assigned. + /// + /// + MultiAddress Listen(MultiAddress address, + Action handler, + CancellationToken cancel); + } +} diff --git a/src/Lib.P2P/Transports/SocketTaskExtensions.cs b/src/Lib.P2P/Transports/SocketTaskExtensions.cs new file mode 100644 index 0000000000..450243821a --- /dev/null +++ b/src/Lib.P2P/Transports/SocketTaskExtensions.cs @@ -0,0 +1,280 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +// Licensed to the .NET Foundation under one or more agreements. +// The .NET Foundation licenses this file to you under the MIT license. +// See the LICENSE file in the project root for more information. + +// SocketTaskExtensions is not available in .Net Framework 4.6.1 +// This was copied and pasted from +// https://searchcode.com/file/115739853/mcs/class/Facades/System.Net.Sockets/SocketTaskExtensions.cs + +#if NET461IGNORE +using System.Collections.Generic; +using System.Threading.Tasks; + +namespace System.Net.Sockets +{ + static class SocketTaskExtensions + { + public static Task AcceptAsync(this Socket socket) + { + return Task.Factory.FromAsync( + (callback, state) => ((Socket)state).BeginAccept(callback, state), + asyncResult => ((Socket)asyncResult.AsyncState).EndAccept(asyncResult), + state: socket); + } + + public static Task AcceptAsync(this Socket socket, Socket acceptSocket) + { + const int ReceiveSize = 0; + return Task.Factory.FromAsync( + (socketForAccept, receiveSize, callback, state) => ((Socket)state).BeginAccept(socketForAccept, receiveSize, callback, state), + asyncResult => ((Socket)asyncResult.AsyncState).EndAccept(asyncResult), + acceptSocket, + ReceiveSize, + state: socket); + } + + public static Task ConnectAsync(this Socket socket, EndPoint remoteEndPoint) + { + return Task.Factory.FromAsync( + (targetEndPoint, callback, state) => ((Socket)state).BeginConnect(targetEndPoint, callback, state), + asyncResult => ((Socket)asyncResult.AsyncState).EndConnect(asyncResult), + remoteEndPoint, + state: socket); + } + + public static Task ConnectAsync(this Socket socket, IPAddress address, int port) + { + return Task.Factory.FromAsync( + (targetAddress, targetPort, callback, state) => ((Socket)state).BeginConnect(targetAddress, targetPort, callback, state), + asyncResult => ((Socket)asyncResult.AsyncState).EndConnect(asyncResult), + address, + port, + state: socket); + } + + public static Task ConnectAsync(this Socket socket, IPAddress[] addresses, int port) + { + return Task.Factory.FromAsync( + (targetAddresses, targetPort, callback, state) => ((Socket)state).BeginConnect(targetAddresses, targetPort, callback, state), + asyncResult => ((Socket)asyncResult.AsyncState).EndConnect(asyncResult), + addresses, + port, + state: socket); + } + + public static Task ConnectAsync(this Socket socket, string host, int port) + { + return Task.Factory.FromAsync( + (targetHost, targetPort, callback, state) => ((Socket)state).BeginConnect(targetHost, targetPort, callback, state), + asyncResult => ((Socket)asyncResult.AsyncState).EndConnect(asyncResult), + host, + port, + state: socket); + } + + public static Task ReceiveAsync(this Socket socket, ArraySegment buffer, SocketFlags socketFlags) + { + return Task.Factory.FromAsync( + (targetBuffer, flags, callback, state) => ((Socket)state).BeginReceive( + targetBuffer.Array, + targetBuffer.Offset, + targetBuffer.Count, + flags, + callback, + state), + asyncResult => ((Socket)asyncResult.AsyncState).EndReceive(asyncResult), + buffer, + socketFlags, + state: socket); + } + + public static Task ReceiveAsync( + this Socket socket, + IList> buffers, + SocketFlags socketFlags) + { + return Task.Factory.FromAsync( + (targetBuffers, flags, callback, state) => ((Socket)state).BeginReceive(targetBuffers, flags, callback, state), + asyncResult => ((Socket)asyncResult.AsyncState).EndReceive(asyncResult), + buffers, + socketFlags, + state: socket); + } + +#if MONO + public static Task ReceiveFromAsync( + this Socket socket, + ArraySegment buffer, + SocketFlags socketFlags, + EndPoint remoteEndPoint) + { + object[] packedArguments = new object[] { socket, remoteEndPoint }; + + return Task.Factory.FromAsync( + (targetBuffer, flags, callback, state) => + { + var arguments = (object[])state; + var s = (Socket)arguments[0]; + var e = (EndPoint)arguments[1]; + + IAsyncResult result = s.BeginReceiveFrom( + targetBuffer.Array, + targetBuffer.Offset, + targetBuffer.Count, + flags, + ref e, + callback, + state); + + arguments[1] = e; + return result; + }, + asyncResult => + { + var arguments = (object[])asyncResult.AsyncState; + var s = (Socket)arguments[0]; + var e = (EndPoint)arguments[1]; + + int bytesReceived = s.EndReceiveFrom(asyncResult, ref e); + + return new SocketReceiveFromResult() + { + ReceivedBytes = bytesReceived, + RemoteEndPoint = e + }; + }, + buffer, + socketFlags, + state: packedArguments); + } + + public static Task ReceiveMessageFromAsync( + this Socket socket, + ArraySegment buffer, + SocketFlags socketFlags, + EndPoint remoteEndPoint) + { + object[] packedArguments = new object[] { socket, socketFlags, remoteEndPoint }; + + return Task.Factory.FromAsync( + (targetBuffer, callback, state) => + { + var arguments = (object[])state; + var s = (Socket)arguments[0]; + var f = (SocketFlags)arguments[1]; + var e = (EndPoint)arguments[2]; + + IAsyncResult result = s.BeginReceiveMessageFrom( + targetBuffer.Array, + targetBuffer.Offset, + targetBuffer.Count, + f, + ref e, + callback, + state); + + arguments[2] = e; + return result; + }, + asyncResult => + { + var arguments = (object[])asyncResult.AsyncState; + var s = (Socket)arguments[0]; + var f = (SocketFlags)arguments[1]; + var e = (EndPoint)arguments[2]; + IPPacketInformation ipPacket; + + int bytesReceived = s.EndReceiveMessageFrom( + asyncResult, + ref f, + ref e, + out ipPacket); + + return new SocketReceiveMessageFromResult() + { + PacketInformation = ipPacket, + ReceivedBytes = bytesReceived, + RemoteEndPoint = e, + SocketFlags = f + }; + }, + buffer, + state: packedArguments); + } +#endif + public static Task SendAsync(this Socket socket, ArraySegment buffer, SocketFlags socketFlags) + { + return Task.Factory.FromAsync( + (targetBuffer, flags, callback, state) => ((Socket)state).BeginSend( + targetBuffer.Array, + targetBuffer.Offset, + targetBuffer.Count, + flags, + callback, + state), + asyncResult => ((Socket)asyncResult.AsyncState).EndSend(asyncResult), + buffer, + socketFlags, + state: socket); + } + + public static Task SendAsync( + this Socket socket, + IList> buffers, + SocketFlags socketFlags) + { + return Task.Factory.FromAsync( + (targetBuffers, flags, callback, state) => ((Socket)state).BeginSend(targetBuffers, flags, callback, state), + asyncResult => ((Socket)asyncResult.AsyncState).EndSend(asyncResult), + buffers, + socketFlags, + state: socket); + } + + public static Task SendToAsync( + this Socket socket, + ArraySegment buffer, + SocketFlags socketFlags, + EndPoint remoteEndPoint) + { + return Task.Factory.FromAsync( + (targetBuffer, flags, endPoint, callback, state) => ((Socket)state).BeginSendTo( + targetBuffer.Array, + targetBuffer.Offset, + targetBuffer.Count, + flags, + endPoint, + callback, + state), + asyncResult => ((Socket)asyncResult.AsyncState).EndSendTo(asyncResult), + buffer, + socketFlags, + remoteEndPoint, + state: socket); + } + } +} +#endif diff --git a/src/Lib.P2P/Transports/Tcp.cs b/src/Lib.P2P/Transports/Tcp.cs new file mode 100644 index 0000000000..2aa1ac35ef --- /dev/null +++ b/src/Lib.P2P/Transports/Tcp.cs @@ -0,0 +1,293 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Runtime.InteropServices; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using MultiFormats; + +namespace Lib.P2P.Transports +{ + /// + /// Establishes a duplex stream between two peers + /// over TCP. + /// + /// + /// determines the network latency and sets the timeout + /// to 3 times the latency or . + /// + public sealed class Tcp : IPeerTransport + { + private static readonly ILog Log = LogManager.GetLogger(typeof(Tcp)); + + /// + /// The minimum read timeout. + /// + /// + /// Defaults to 3 seconds. + /// + private static readonly TimeSpan MinReadTimeout = TimeSpan.FromSeconds(3); + + /// + public async Task ConnectAsync(MultiAddress address, + CancellationToken cancel = default) + { + var port = address.Protocols + .Where(p => p.Name == "tcp") + .Select(p => int.Parse(p.Value)) + .First(); + + var ip = address.Protocols + .FirstOrDefault(p => p.Name == "ip4" || p.Name == "ip6"); + + if (ip == null) + { + throw new ArgumentException($"Missing IP address in '{address}'.", nameof(address)); + } + + Socket socket = new( + ip.Name == "ip4" ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6, + SocketType.Stream, + ProtocolType.Tcp); + + var latency = MinReadTimeout; // keep compiler happy + var start = DateTime.Now; + + try + { + Log.Trace("connecting to " + address); + + // Handle cancellation of the connect attempt by disposing + // of the socket. This will force ConnectAsync to return. + await using (var _ = cancel.Register(() => + { + if (socket != null) + { + socket?.Dispose(); + } + + socket = null; + })) + { + var ipaddr = IPAddress.Parse(ip.Value); + await socket.ConnectAsync(ipaddr, port).ConfigureAwait(false); + } + + latency = DateTime.Now - start; + Log.Trace($"connected to {address} in {latency.TotalMilliseconds} ms"); + } + catch (Exception) when (cancel.IsCancellationRequested) + { + // eat it, the caller has cancelled and doesn't care. + } + catch (Exception) + { + latency = DateTime.Now - start; + Log.Trace($"failed to {address} in {latency.TotalMilliseconds} ms"); + socket?.Dispose(); + throw; + } + + if (cancel.IsCancellationRequested) + { + Log.Trace("cancel " + address); + socket?.Dispose(); + cancel.ThrowIfCancellationRequested(); + } + + var timeout = (int) Math.Max(MinReadTimeout.TotalMilliseconds, latency.TotalMilliseconds * 3); + socket.LingerState = new LingerOption(false, 0); + socket.ReceiveTimeout = timeout; + socket.SendTimeout = timeout; + Stream stream = new NetworkStream(socket, true); + stream.ReadTimeout = timeout; + stream.WriteTimeout = timeout; + + stream = new DuplexBufferedStream(stream); + + if (!cancel.IsCancellationRequested) + { + return stream; + } + + Log.Trace("cancel " + address); + await stream.DisposeAsync(); + cancel.ThrowIfCancellationRequested(); + + return stream; + } + + /// + public MultiAddress Listen(MultiAddress address, + Action handler, + CancellationToken cancel) + { + var port = address.Protocols + .Where(p => p.Name == "tcp") + .Select(p => int.Parse(p.Value)) + .FirstOrDefault(); + + var ip = address.Protocols + .FirstOrDefault(p => p.Name == "ip4" || p.Name == "ip6"); + + if (ip == null) + { + throw new ArgumentException($"Missing IP address in '{address}'.", nameof(address)); + } + + var ipAddress = IPAddress.Parse(ip.Value); + IPEndPoint endPoint = new(ipAddress, port); + Socket socket = new( + endPoint.AddressFamily, + SocketType.Stream, + ProtocolType.Tcp); + try + { + socket.Bind(endPoint); + socket.Listen(100); + } + catch (Exception e) + { + socket.Dispose(); + throw new Exception("Bind/listen failed on " + address, e); + } + + // If no port specified, then add it. + var actualPort = ((IPEndPoint) socket.LocalEndPoint).Port; + + if (port != actualPort) + { + address = address.Clone(); + var protocol = address.Protocols.FirstOrDefault(p => p.Name == "tcp"); + if (protocol != null) + { + protocol.Value = actualPort.ToString(); + } + else + { + address.Protocols.AddRange(new MultiAddress("/tcp/" + actualPort).Protocols); + } + } + + _ = Task.Run(() => ProcessConnection(socket, address, handler, cancel), cancel); + + return address; + } + + private static void ProcessConnection(Socket socket, + MultiAddress address, + Action handler, + CancellationToken cancel) + { + Log.Debug("listening on " + address); + + // Handle cancellation of the listener + cancel.Register(() => + { + Log.Debug("Got cancel on " + address); + + try + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + socket.Shutdown(SocketShutdown.Both); + socket.Dispose(); + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + socket.Shutdown(SocketShutdown.Receive); + } + else // must be windows + { + socket.Dispose(); + } + } + catch (Exception e) + { + Log.Warn($"Cancelling listener: {e.Message}"); + } + finally + { + socket = null; + } + }); + + try + { + while (!cancel.IsCancellationRequested) + { + var conn = socket.Accept(); + + MultiAddress remote = null; + if (conn.RemoteEndPoint is IPEndPoint endPoint) + { + StringBuilder s = new(); + s.Append(endPoint.AddressFamily == AddressFamily.InterNetwork ? "/ip4/" : "/ip6/"); + s.Append(endPoint.Address); + s.Append("/tcp/"); + s.Append(endPoint.Port); + remote = new MultiAddress(s.ToString()); + Log.Debug("connection from " + remote); + } + + conn.NoDelay = true; + Stream peer = new NetworkStream(conn, true); + + peer = new DuplexBufferedStream(peer); + + try + { + handler(peer, address, remote); + } + catch (Exception e) + { + Log.Error("listener handler failed " + address, e); + peer.Dispose(); + } + } + } + catch (Exception) when (cancel.IsCancellationRequested) + { + // ignore + } + catch (Exception e) + { + Console.WriteLine(e.Message); + Log.Error("listener failed " + address, e); + } + finally + { + socket?.Dispose(); + } + + Log.Debug("stop listening on " + address); + } + } +} diff --git a/src/Lib.P2P/Transports/TransportRegistry.cs b/src/Lib.P2P/Transports/TransportRegistry.cs new file mode 100644 index 0000000000..64884678b6 --- /dev/null +++ b/src/Lib.P2P/Transports/TransportRegistry.cs @@ -0,0 +1,47 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; + +namespace Lib.P2P.Transports +{ + internal static class TransportRegistry + { + public static Dictionary> Transports; + + static TransportRegistry() + { + Transports = new Dictionary>(); + Register("tcp", () => new Tcp()); + Register("udp", () => new Udp()); + } + + public static void Register(string protocolName, Func transport) + { + Transports.Add(protocolName, transport); + } + + public static void Deregister(string protocolName) { Transports.Remove(protocolName); } + } +} diff --git a/src/Lib.P2P/Transports/Udp.cs b/src/Lib.P2P/Transports/Udp.cs new file mode 100644 index 0000000000..b6a6898911 --- /dev/null +++ b/src/Lib.P2P/Transports/Udp.cs @@ -0,0 +1,135 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Threading; +using System.Threading.Tasks; +using Common.Logging; +using MultiFormats; + +namespace Lib.P2P.Transports +{ + /// + /// Establishes a duplex stream between two peers + /// over UDP. + /// + public class Udp : IPeerTransport + { + private static ILog _log = LogManager.GetLogger(typeof(Udp)); + + /// + public async Task ConnectAsync(MultiAddress address, + CancellationToken cancel = default) + { + var port = address.Protocols + .Where(p => p.Name == "udp") + .Select(p => int.Parse(p.Value)) + .First(); + var ip = address.Protocols + .Where(p => p.Name == "ip4" || p.Name == "ip6") + .First(); + Socket socket = new( + ip.Name == "ip4" ? AddressFamily.InterNetwork : AddressFamily.InterNetworkV6, + SocketType.Dgram, + ProtocolType.Udp); + + // Handle cancellation of the connect attempt + cancel.Register(() => + { + socket.Dispose(); + socket = null; + }); + + try + { + _log.Debug("connecting to " + address); + await socket.ConnectAsync(ip.Value, port).ConfigureAwait(false); + _log.Debug("connected " + address); + } + catch (Exception) when (cancel.IsCancellationRequested) + { + // eat it, the caller has cancelled and doesn't care. + } + catch (Exception e) + { + _log.Warn("failed " + address, e); + throw; + } + + if (cancel.IsCancellationRequested) + { + _log.Debug("cancel " + address); + socket?.Dispose(); + cancel.ThrowIfCancellationRequested(); + } + + return new DatagramStream(socket, true); + } + + /// + public MultiAddress Listen(MultiAddress address, + Action handler, + CancellationToken cancel) + { + var port = address.Protocols + .Where(p => p.Name == "udp") + .Select(p => int.Parse(p.Value)) + .FirstOrDefault(); + var ip = address.Protocols + .Where(p => p.Name == "ip4" || p.Name == "ip6") + .First(); + var ipAddress = IPAddress.Parse(ip.Value); + IPEndPoint endPoint = new(ipAddress, port); + Socket socket = new( + endPoint.AddressFamily, + SocketType.Dgram, + ProtocolType.Udp); + socket.Bind(endPoint); + + // If no port specified, then add it. + var actualPort = ((IPEndPoint) socket.LocalEndPoint).Port; + if (port != actualPort) + { + address = address.Clone(); + var protocol = address.Protocols.FirstOrDefault(p => p.Name == "udp"); + if (protocol != null) + protocol.Value = actualPort.ToString(); + else + address.Protocols.AddRange(new MultiAddress("/udp/" + actualPort).Protocols); + } + + // TODO: UDP listener + throw new NotImplementedException(); +#if false + var stream = new DatagramStream(socket, ownsSocket: true); + handler(stream, address, null); + + return address; +#endif + } + } +} diff --git a/src/Lib.P2P/WhiteList.cs b/src/Lib.P2P/WhiteList.cs new file mode 100644 index 0000000000..b194464faa --- /dev/null +++ b/src/Lib.P2P/WhiteList.cs @@ -0,0 +1,46 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Concurrent; +using System.Linq; + +namespace Lib.P2P +{ + /// + /// A sequence of targets that are approved. + /// + /// + /// The type of object that the rule applies to. + /// + /// + /// Only targets that are defined will pass. If no targets are defined, then anything + /// passes. + /// + public class WhiteList : ConcurrentBag, IPolicy + where T : IEquatable + { + /// + public bool IsAllowed(T target) { return IsEmpty || this.Contains(target); } + } +} diff --git a/src/Lib.P2P/doc/.gitignore b/src/Lib.P2P/doc/.gitignore new file mode 100644 index 0000000000..a9085c88fe --- /dev/null +++ b/src/Lib.P2P/doc/.gitignore @@ -0,0 +1,13 @@ +############### +# folder # +############### +/**/DROP/ +/**/TEMP/ +/**/packages/ +/**/bin/ +/**/obj/ +_site +log.txt +msdn.4.5.2.zip +namespaces.4.5.2.zip + diff --git a/src/Lib.P2P/doc/Documentation.csproj b/src/Lib.P2P/doc/Documentation.csproj new file mode 100644 index 0000000000..18e9ef1085 --- /dev/null +++ b/src/Lib.P2P/doc/Documentation.csproj @@ -0,0 +1,11 @@ + + + + net6.0 + + + + + + + diff --git a/src/Lib.P2P/doc/articles/dht/findpeer.md b/src/Lib.P2P/doc/articles/dht/findpeer.md new file mode 100644 index 0000000000..565fffa8a8 --- /dev/null +++ b/src/Lib.P2P/doc/articles/dht/findpeer.md @@ -0,0 +1,14 @@ +# Finding a peer + +If a peer's ID is known but not it's addresses, then [Dht.FindPeerAsync](xref:PeerTalk.Routing.Dht1.FindPeerAsync*) can be used. +It will ask other peers for the details of the unknown peer. + +```csharp +var peerId = new MultiHash("QmdpwjdB94eNm2Lcvp9JqoCxswo3AKQqjLuNZyLixmCxxx"); +peer = await dht.FindPeerAsync(peerId); +``` + +## Algorithm + +A [DHT distributed query](xref:PeerTalk.Routing.DistributedQuery`1) is created that iteratively asks the closest +peers to the unknown peer for its details. \ No newline at end of file diff --git a/src/Lib.P2P/doc/articles/disco.md b/src/Lib.P2P/doc/articles/disco.md new file mode 100644 index 0000000000..b14fbc5175 --- /dev/null +++ b/src/Lib.P2P/doc/articles/disco.md @@ -0,0 +1,38 @@ +# Peer Discovery + +Various schemes are used to find other peers in the network. + +To get the discovered peers use the [Swarm.KnownPeers](xref:PeerTalk.Swarm.KnownPeers) +property. + +```csharp +foreach (var peer in swarm.KnownPeers) +{ + Console.WriteLine(peer.Id); +} +``` + +## Schemes + +### Multicast DNS + +[MdnsNext](xref:PeerTalk.Discovery.MdnsNext) uses [RFC 6762 - Multicast DNS](https://tools.ietf.org/html/rfc6762) +to locate peers on the local area network with zero configuration. +Local area network peers are very useful to peer-to-peer protocols, because of their low latency links. + +The [mDNS-discovery](https://github.com/libp2p/specs/blob/master/discovery/mdns.md) specification +describes how IPFS uses mDNS to discover other peers; [private networks](pnet.md) are also supported. + +### Bootstrap + +[Bootstrap](xref:PeerTalk.Discovery.Bootstrap) uses a pre-configured list +of addresses of highly stable (and somewhat trusted) peers to +find the rest of the network. These is sometimes referred to as `seed nodes`. + +### Randon walk + +The `Ipfs.Engine` makes random DHT (Distributed Hash Table) [queries](xref:Ipfs.CoreApi.IPeerRouting.FindPeerAsync*) +in order to discover a large number of peers quickly. +This causes the DHT to converge much faster, at the expense of a small load +at the very beginning. + diff --git a/src/Lib.P2P/doc/articles/intro.md b/src/Lib.P2P/doc/articles/intro.md new file mode 100644 index 0000000000..cfda3c68ac --- /dev/null +++ b/src/Lib.P2P/doc/articles/intro.md @@ -0,0 +1,11 @@ +# Peer Talk + +A collection of peer-to-peer protocols in the spirit of [libp2p](https://github.com/libp2p/libp2p). + +The source code is on [GitHub](https://github.com/richardschneider/peer-talk) and the +package is published on [NuGet](https://www.nuget.org/packages/PeerTalk). + +## Related projects + +- [IPFS Core](https://github.com/richardschneider/net-ipfs-core) +- [IPFS Engine](https://github.com/richardschneider/net-ipfs-engine) diff --git a/src/Lib.P2P/doc/articles/peer.md b/src/Lib.P2P/doc/articles/peer.md new file mode 100644 index 0000000000..f6353b544b --- /dev/null +++ b/src/Lib.P2P/doc/articles/peer.md @@ -0,0 +1,25 @@ +# Peer + +A [peer](xref:Ipfs.Peer) is a node in the IPFS network; the [Dfs](https://github.com/richardschneider/net-ipfs-engine) is a C# implementation. +Among other properties, it has an [unique identifier](xref:Ipfs.Peer.Id) and [addresses](xref:Ipfs.Peer.Addresses) that it responds to. + +## Sending a message + +To talk to a peer, the [Swarm](xref:PeerTalk.Swarm.DialAsync*) is used to establish a +connection and then select a [protocol](http://todo). + +```csharp +using (var stream = await swarm.DialAsync(peer, "/ipfs/id/1.0.0")) +{ + // Send a message to the stream +} +``` + +## Receiving a message + +To receive a message from a peer, a protocol handler is registered with the +[Swarm](xref:PeerTalk.Swarm.AddProtocol*) + +```csharp +TODO +``` diff --git a/src/Lib.P2P/doc/articles/pnet.md b/src/Lib.P2P/doc/articles/pnet.md new file mode 100644 index 0000000000..f743b85c90 --- /dev/null +++ b/src/Lib.P2P/doc/articles/pnet.md @@ -0,0 +1,23 @@ +# Private Network + +A private network is a group of [peers](peer.md) that share the same 256-bit +[secret key](xref:PeerTalk.Cryptography.PreSharedKey). All communication between the +peers is encryped with the [XSalsa20 cipher](https://en.wikipedia.org/wiki/Salsa20). The +specification is at [PSK v1](https://github.com/libp2p/specs/blob/master/pnet/Private-Networks-PSK-V1.md) +and is implemented by the [Psk1Protector](xref:PeerTalk.SecureCommunication.Psk1Protector) class. + +The private network is defined by the symmetric secret key, which is known by all members. +All traffic leaving the peer is encrypted and there is no characteristic +handshake. The secret key is just a random number; public/private +keys and certificates are not needed. + +## Joining + +The local peer becomes a member of the private network by having the +secret key and adding it to the [Swarm.NetworkProtector](xref:PeerTalk.Swarm.NetworkProtector) + +```csharp +var key = new PreSharedKey { Value = "e8d6...".ToHexBuffer() }; +var protector = new Psk1Protector { Key = key } +swarm.NetworkProtector = protector; +``` diff --git a/src/Lib.P2P/doc/articles/toc.yml b/src/Lib.P2P/doc/articles/toc.yml new file mode 100644 index 0000000000..92dabf2084 --- /dev/null +++ b/src/Lib.P2P/doc/articles/toc.yml @@ -0,0 +1,13 @@ +- name: Introduction + href: intro.md +- name: Peer + href: peer.md + items: + - name: Discovery + href: disco.md + - name: Finding addresses + href: dht/findpeer.md +- name: Private Network + href: pnet.md +- name: Class Reference + href: ../api/PeerTalk.yml \ No newline at end of file diff --git a/src/Lib.P2P/doc/docfx.json b/src/Lib.P2P/doc/docfx.json new file mode 100644 index 0000000000..fe86ebb133 --- /dev/null +++ b/src/Lib.P2P/doc/docfx.json @@ -0,0 +1,89 @@ +{ + "metadata": [ + { + "src": [ + { + "files": [ + "src/**.csproj" + ], + "exclude": [ + "**/obj/**", + "**/bin/**", + "_site/**" + ], + "src": ".." + } + ], + "properties": { + "TargetFramework": "net6.0" + }, + + "dest": "api" + } + ], + "build": { + "content": [ + { + "files": [ + "api/**.yml", + "api/index.md" + ] + }, + { + "files": [ + "articles/**.md", + "articles/**/toc.yml", + "toc.yml", + "*.md" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "resource": [ + { + "files": [ + "images/**" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "overwrite": [ + { + "files": [ + "apidoc/**.md" + ], + "exclude": [ + "obj/**", + "_site/**" + ] + } + ], + "xrefService": [ + "https://xref.docs.microsoft.com/query?uid={uid}" + ], + "xref": [ + "https://richardschneider.github.io/net-ipfs-core/xrefmap.yml", + "https://richardschneider.github.io/net-mdns/xrefmap.yml" + ], + "globalMetadata": { + "_appTitle": "Peer Talk documentation", + "_appFooter": "Generated by DocFX", + "_appFaviconPath": "images/peertalk.ico", + "_appLogoPath": "images/peertalk.svg" + }, + "dest": "_site", + "globalMetadataFiles": [], + "fileMetadataFiles": [], + "template": [ + "default" + ], + "postProcessors": [], + "noLangKeyword": false + } +} diff --git a/src/Lib.P2P/doc/images/atlascity.io-logo.png b/src/Lib.P2P/doc/images/atlascity.io-logo.png new file mode 100644 index 0000000000..6c61b6aa76 Binary files /dev/null and b/src/Lib.P2P/doc/images/atlascity.io-logo.png differ diff --git a/src/Lib.P2P/doc/images/docs-latest-green.svg b/src/Lib.P2P/doc/images/docs-latest-green.svg new file mode 100644 index 0000000000..1cd064a3d7 --- /dev/null +++ b/src/Lib.P2P/doc/images/docs-latest-green.svg @@ -0,0 +1 @@ +docsdocslatestlatest \ No newline at end of file diff --git a/src/Lib.P2P/doc/images/peertalk-200x200.png b/src/Lib.P2P/doc/images/peertalk-200x200.png new file mode 100644 index 0000000000..ae182a7d12 Binary files /dev/null and b/src/Lib.P2P/doc/images/peertalk-200x200.png differ diff --git a/src/Lib.P2P/doc/images/peertalk-32x32.png b/src/Lib.P2P/doc/images/peertalk-32x32.png new file mode 100644 index 0000000000..ef0c2fe67c Binary files /dev/null and b/src/Lib.P2P/doc/images/peertalk-32x32.png differ diff --git a/src/Lib.P2P/doc/images/peertalk-64x64.png b/src/Lib.P2P/doc/images/peertalk-64x64.png new file mode 100644 index 0000000000..59f6b8a889 Binary files /dev/null and b/src/Lib.P2P/doc/images/peertalk-64x64.png differ diff --git a/src/Lib.P2P/doc/images/peertalk.ico b/src/Lib.P2P/doc/images/peertalk.ico new file mode 100644 index 0000000000..04a5263493 Binary files /dev/null and b/src/Lib.P2P/doc/images/peertalk.ico differ diff --git a/src/Lib.P2P/doc/images/peertalk.svg b/src/Lib.P2P/doc/images/peertalk.svg new file mode 100644 index 0000000000..a1fe434fb9 --- /dev/null +++ b/src/Lib.P2P/doc/images/peertalk.svg @@ -0,0 +1,4 @@ + + Group \ No newline at end of file diff --git a/src/Lib.P2P/doc/index.md b/src/Lib.P2P/doc/index.md new file mode 100644 index 0000000000..8834fe1f8d --- /dev/null +++ b/src/Lib.P2P/doc/index.md @@ -0,0 +1,10 @@ +# Peer Talk + +A collection of peer-to-peer protocols in the spirit of [libp2p](https://github.com/libp2p/libp2p). +The source code is on [GitHub](https://github.com/richardschneider/peer-talk) and the +package is published on [NuGet](https://www.nuget.org/packages/PeerTalk). + + +- [Articles](articles/intro.md) on using Peer Talk +- [Class Reference](api/PeerTalk.yml) describes the classes in detail + diff --git a/src/Lib.P2P/doc/toc.yml b/src/Lib.P2P/doc/toc.yml new file mode 100644 index 0000000000..823e243ae0 --- /dev/null +++ b/src/Lib.P2P/doc/toc.yml @@ -0,0 +1,6 @@ +- name: Articles + href: articles/ +- name: Api Documentation + href: api/ +- name: Github + href: https://github.com/richardschneider/peer-talk diff --git a/src/Lib.P2P/docs/message.md b/src/Lib.P2P/docs/message.md new file mode 100644 index 0000000000..66d9774cbd --- /dev/null +++ b/src/Lib.P2P/docs/message.md @@ -0,0 +1,17 @@ +# Message + +Messages are exchanged between peers to request some action. Each message consists of +- A [Varint](xref:Ipfs.Varint) length prefix +- The payload +- A terminating newline (0x0a) + +The `length prefix` is the payload size + 1 (for the newline) + +## Example + +The wire representation of the 'foo' message +``` + 0x04 - the length prefix + 0x66 0x6f 0x6f - the payload + 0x0a - the terminating newline +``` \ No newline at end of file diff --git a/src/Lib.P2P/handshake.md b/src/Lib.P2P/handshake.md new file mode 100644 index 0000000000..860dff2152 --- /dev/null +++ b/src/Lib.P2P/handshake.md @@ -0,0 +1,60 @@ +# JS Handshake + +This documents the handshake message exchange when a peer (local) connects to another peer (remote). + +It was traced with WireShark on Feb 11 2018 using jsipfs v0.27.07 by doing + +``` +jsipfs daemon +jsipfs swarm connect /ip4/127.0.0.1/tcp/4002/ipfs/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb +``` + +The daemon's ID +``` +> jsipfs id +{ + "id": "QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb", + "publicKey": "CAASpgIwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCfBYU9c0n28u02N/XCJY8yIsRqRVO5Zw+6kDHCremt2flHT4AaWnwGLAG9YyQJbRTvWN9nW2LK7Pv3uoIlvUSTnZEP0SXB5oZeqtxUdi6tuvcyqTIfsUSanLQucYITq8Qw3IMBzk+KpWNm98g9A/Xy30MkUS8mrBIO9pHmIZa55fvclDkTvLxjnGWA2avaBfJvHgMSTu0D2CQcmJrvwyKMhLCSIbQewZd2V7vc6gtxbRovKlrIwDTmDBXbfjbLljOuzg2yBLyYxXlozO9blpttbnOpU4kTspUVJXglmjsv7YSIJS3UKt3544l/srHbqlwC5CgOgjlwNfYPadO8kmBfAgMBAAE=", + "addresses": [ + "/ip4/127.0.0.1/tcp/4002/ipfs/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb", + "/ip4/127.0.0.1/tcp/4003/ws/ipfs/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb", + "/ip4/169.254.140.225/tcp/4002/ipfs/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb", + "/ip4/192.168.178.21/tcp/4002/ipfs/QmXFX2P5ammdmXQgfqGkfswtEVFsZUJ5KeHRXQYCTdiTAb" + ], + "agentVersion": "js-ipfs/0.27.7", + "protocolVersion": "9000" +} +``` + +## Messages + +Note the messages are not displayed with the length prefix nor newline suffix. + +| Local (port 58584)| Remote (port 4002) | +| ----- | ------ | +| /multistream/1.0.0 | | +| | /multistream/1.0.0 | +| /plaintext/1.0.0 | | +| | /plaintext/1.0.0 | +| /multistream/1.0.0 | | +| | /multistream/1.0.0 | +| /mplex/6.7.0 | | +| | /mplex/6.7.0 | +| | 0x08 0x00 - mplex create stream 1 no name | +| | 0x0a 0x14 - mplex message initiator stream 1 +| | /multistream/1.0.0 | +| 0x09 0x14 | | +| /multistream/1.0.0 | | +| | 0x09 0x14 +| | /multistream/1.0.0 | +| 0x0a 0x10 | | +| | /ipfs/id/1.0.0 | +| 0x09 0x10 | | +| ipfs/id/1.0.0 | | +| 0x09 0xad 0x04 | | +| ... maybe addresses | | +| | 0e 00 | + +# GO Handshake + +ipfs daemon --disable-transport-encryption diff --git a/src/MultiFormats.Tests/Base32Test.cs b/src/MultiFormats.Tests/Base32Test.cs new file mode 100644 index 0000000000..f1b19aef2e --- /dev/null +++ b/src/MultiFormats.Tests/Base32Test.cs @@ -0,0 +1,85 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MultiFormats.Tests +{ + [TestClass] + public class Base32EncodeTests + { + private byte[] GetStringBytes(string x) { return Encoding.ASCII.GetBytes(x); } + + [TestMethod] + public void Vector1() { Assert.AreEqual(string.Empty, Base32.Encode(GetStringBytes(string.Empty))); } + + [TestMethod] + public void Vector2() { Assert.AreEqual("my", Base32.Encode(GetStringBytes("f"))); } + + [TestMethod] + public void Vector3() { Assert.AreEqual("mzxq", Base32.Encode(GetStringBytes("fo"))); } + + [TestMethod] + public void Vector4() { Assert.AreEqual("mzxw6", Base32.Encode(GetStringBytes("foo"))); } + + [TestMethod] + public void Vector5() { Assert.AreEqual("mzxw6yq", Base32.Encode(GetStringBytes("foob"))); } + + [TestMethod] + public void Vector6() { Assert.AreEqual("mzxw6ytb", Base32.Encode(GetStringBytes("fooba"))); } + + [TestMethod] + public void Vector7() { Assert.AreEqual("mzxw6ytboi", Base32.Encode(GetStringBytes("foobar"))); } + } + + [TestClass] + public class Base32DecodeTests + { + private byte[] GetStringBytes(string x) { return Encoding.ASCII.GetBytes(x); } + + [TestMethod] + public void Vector1() { CollectionAssert.AreEqual(GetStringBytes(string.Empty), Base32.Decode(string.Empty)); } + + [TestMethod] + public void Vector2() { CollectionAssert.AreEqual(GetStringBytes("f"), Base32.Decode("MY======")); } + + [TestMethod] + public void Vector3() { CollectionAssert.AreEqual(GetStringBytes("fo"), Base32.Decode("MZXQ====")); } + + [TestMethod] + public void Vector4() { CollectionAssert.AreEqual(GetStringBytes("foo"), Base32.Decode("MZXW6===")); } + + [TestMethod] + public void Vector5() { CollectionAssert.AreEqual(GetStringBytes("foob"), Base32.Decode("MZXW6YQ=")); } + + [TestMethod] + public void Vector6() { CollectionAssert.AreEqual(GetStringBytes("fooba"), Base32.Decode("MZXW6YTB")); } + + [TestMethod] + public void Vector7() + { + CollectionAssert.AreEqual(GetStringBytes("foobar"), Base32.Decode("MZXW6YTBOI======")); + } + } +} diff --git a/src/MultiFormats.Tests/Base58Test.cs b/src/MultiFormats.Tests/Base58Test.cs new file mode 100644 index 0000000000..eadd1b2d09 --- /dev/null +++ b/src/MultiFormats.Tests/Base58Test.cs @@ -0,0 +1,74 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MultiFormats.Tests +{ + [TestClass] + public class Base58Test + { + [TestMethod] + public void Encode() + { + Assert.AreEqual("jo91waLQA1NNeBmZKUF", Base58.Encode(Encoding.UTF8.GetBytes("this is a test"))); + Assert.AreEqual("jo91waLQA1NNeBmZKUF", Encoding.UTF8.GetBytes("this is a test").ToBase58()); + } + + [TestMethod] + public void Decode() + { + Assert.AreEqual("this is a test", Encoding.UTF8.GetString(Base58.Decode("jo91waLQA1NNeBmZKUF"))); + Assert.AreEqual("this is a test", Encoding.UTF8.GetString("jo91waLQA1NNeBmZKUF".FromBase58())); + } + + /// + /// C# version of base58Test in + /// + [TestMethod] + public void Java() + { + var input = "QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB"; + var output = Base58.Decode(input); + var encoded = Base58.Encode(output); + Assert.AreEqual(input, encoded); + } + + [TestMethod] + public void Decode_Bad() + { + ExceptionAssert.Throws(() => Base58.Decode("jo91waLQA1NNeBmZKUF==")); + } + + [TestMethod] + public void Zero() + { + Assert.AreEqual("1111111", Base58.Encode(new byte[7])); + Assert.AreEqual(7, Base58.Decode("1111111").Length); + Assert.IsTrue(Base58.Decode("1111111").All(b => b == 0)); + } + } +} diff --git a/src/MultiFormats.Tests/Cryptography/HashingTest.cs b/src/MultiFormats.Tests/Cryptography/HashingTest.cs new file mode 100644 index 0000000000..f4e44e8e6c --- /dev/null +++ b/src/MultiFormats.Tests/Cryptography/HashingTest.cs @@ -0,0 +1,201 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MultiFormats.Tests.Cryptography +{ + [TestClass] + public sealed class HashingTest + { + private static readonly string Merkle = Encoding.UTF8.GetBytes("Merkle–Damgård").ToHexString(); + + private sealed class TestVector + { + public string Algorithm { get; set; } + public string Input { get; set; } + public string Digest { get; set; } + } + + private TestVector[] TestVectors = + { + new TestVector + { + Algorithm = "sha1", + Input = "68656c6c6f", // "hello" in hex + Digest = "aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d" + }, + + // From https://bitcoin.stackexchange.com/questions/5671/how-do-you-perform-double-sha-256-encoding/5677 + new TestVector + { + Algorithm = "sha2-256", + Input = "68656c6c6f", // "hello" in hex + Digest = "2cf24dba5fb0a30e26e83b2ac5b9e29e1b161e5c1fa7425e73043362938b9824" + }, + new TestVector + { + Algorithm = "dbl-sha2-256", + Input = "68656c6c6f", // "hello" in hex + Digest = "9595c9df90075148eb06860365df33584b75bff782a510c6cd4883a419833d50" + }, + + // From + new TestVector + { + Algorithm = "keccak-512", + Input = "", + Digest = + "0eab42de4c3ceb9235fc91acffe746b29c29a8c366b7c60e4e67c466f36a4304c00fa9caf9d87976ba469bcbe06713b435f091ef2769fb160cdab33d3670680e" + }, + + // From https://csrc.nist.gov/Projects/Cryptographic-Algorithm-Validation-Program/Secure-Hashing#sha3vsha3vss + new TestVector + { + Algorithm = "sha3-512", + Input = "", + Digest = + "a69f73cca23a9ac5c8b567dc185a756e97c982164fe25859e0d1dcc1475c80a615b2123af1f5f94c11e3e9402c3ac558f500199d95b6d3e301758586281dcd26" + }, + new TestVector + { + Algorithm = "sha3-512", + Input = "37d518", + Digest = + "4aa96b1547e6402c0eee781acaa660797efe26ec00b4f2e0aec4a6d10688dd64cbd7f12b3b6c7f802e2096c041208b9289aec380d1a748fdfcd4128553d781e3" + }, + new TestVector + { + Algorithm = "shake-128", + Input = "", + Digest = "7f9c2ba4e88f827d616045507605853e" + }, + new TestVector + { + Algorithm = "shake-128", + Input = "0e", + Digest = "fa996dafaa208d72287c23bc4ed4bfd5" + }, + new TestVector + { + Algorithm = "shake-128", + Input = + "fd6dd3b63dc7b9664895c51fc17c57d59c349621dd3c5694a3cc404c660c2cc47d83d2f0e3d2a28a3aa2f0a710db54", + Digest = "c8db32bf81bf75621db30264750954f8" + }, + + // From https://en.wikipedia.org/wiki/MD4 + new TestVector + { + Algorithm = "md4", + Input = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog").ToHexString(), + Digest = "1bee69a46ba811185c194762abaeae90", + }, + + // From https://en.wikipedia.org/wiki/MD5 + new TestVector + { + Algorithm = "md5", + Input = Encoding.UTF8.GetBytes("The quick brown fox jumps over the lazy dog").ToHexString(), + Digest = "9e107d9d372bb6826bd81d3542a419d6", + }, + new TestVector + { + Algorithm = "identity", + Input = "", + Digest = "" + }, + new TestVector + { + Algorithm = "identity", + Input = "00", + Digest = "00" + }, + new TestVector + { + Algorithm = "identity", + Input = "0001", + Digest = "0001" + }, + new TestVector + { + Algorithm = "id", + Input = "0001", + Digest = "0001" + }, + + // From https://github.com/multiformats/website/blob/master/content/multihash.md#examples + new TestVector + { + Algorithm = "sha1", Input = Merkle, Digest = "8a173fd3e32c0fa78b90fe42d305f202244e2739" + }, + new TestVector + { + Algorithm = "sha2-256", Input = Merkle, + Digest = "41dd7b6443542e75701aa98a0c235951a28a0d851b11564d20022ab11d2589a8" + }, + new TestVector + { + Algorithm = "sha2-512", Input = Merkle, + Digest = + "52eb4dd19f1ec522859e12d89706156570f8fbab1824870bc6f8c7d235eef5f4c2cbbafd365f96fb12b1d98a0334870c2ce90355da25e6a1108a6e17c4aaebb0" + }, + new TestVector + { + Algorithm = "blake2b-512", Input = Merkle, + Digest = + "d91ae0cb0e48022053ab0f8f0dc78d28593d0f1c13ae39c9b169c136a779f21a0496337b6f776a73c1742805c1cc15e792ddb3c92ee1fe300389456ef3dc97e2" + }, + new TestVector + { + Algorithm = "blake2b-256", Input = Merkle, + Digest = "7d0a1371550f3306532ff44520b649f8be05b72674e46fc24468ff74323ab030" + }, + new TestVector + { + Algorithm = "blake2s-256", Input = Merkle, + Digest = "a96953281f3fd944a3206219fad61a40b992611b7580f1fa091935db3f7ca13d" + }, + new TestVector + { + Algorithm = "blake2s-128", Input = Merkle, Digest = "0a4ec6f1629e49262d7093e2f82a3278" + }, + }; + + /// + /// Test vectors from various sources. + /// + [TestMethod] + public void CheckHashes() + { + foreach (var v in TestVectors) + { + var actual = MultiHash + .GetHashAlgorithm(v.Algorithm) + .ComputeHash(v.Input.ToHexBuffer()); + Assert.AreEqual(v.Digest, actual.ToHexString(), $"{v.Algorithm} for '{v.Input}'"); + } + } + } +} diff --git a/src/MultiFormats.Tests/ExceptionAssert.cs b/src/MultiFormats.Tests/ExceptionAssert.cs new file mode 100644 index 0000000000..955f57c62f --- /dev/null +++ b/src/MultiFormats.Tests/ExceptionAssert.cs @@ -0,0 +1,71 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MultiFormats.Tests +{ + /// + /// Asserting an . + /// + public static class ExceptionAssert + { + public static T Throws(Action action, string expectedMessage = null) where T : Exception + { + try + { + action(); + } + catch (AggregateException e) + { + var match = e.InnerExceptions.OfType().FirstOrDefault(); + if (match != null) + { + if (expectedMessage != null) + Assert.AreEqual(expectedMessage, match.Message, "Wrong exception message."); + return match; + } + + throw; + } + catch (T e) + { + if (expectedMessage != null) + Assert.AreEqual(expectedMessage, e.Message); + return e; + } + + Assert.Fail("Exception of type {0} should be thrown.", typeof(T)); + + // The compiler doesn't know that Assert.Fail will always throw an exception + return null; + } + + public static Exception Throws(Action action, string expectedMessage = null) + { + return Throws(action, expectedMessage); + } + } +} diff --git a/src/MultiFormats.Tests/HexStringTest.cs b/src/MultiFormats.Tests/HexStringTest.cs new file mode 100644 index 0000000000..9dae3601c1 --- /dev/null +++ b/src/MultiFormats.Tests/HexStringTest.cs @@ -0,0 +1,71 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MultiFormats.Tests +{ + [TestClass] + public sealed class HexStringTest + { + [TestMethod] + public void Encode() + { + var buffer = Enumerable.Range(byte.MinValue, byte.MaxValue).Select(b => (byte) b).ToArray(); + var lowerHex = string.Concat(buffer.Select(b => b.ToString("x2")).ToArray()); + var upperHex = string.Concat(buffer.Select(b => b.ToString("X2")).ToArray()); + + Assert.AreEqual(lowerHex, buffer.ToHexString(), "encode default"); + Assert.AreEqual(lowerHex, buffer.ToHexString(), "encode general"); + Assert.AreEqual(lowerHex, buffer.ToHexString("x"), "encode lower"); + Assert.AreEqual(upperHex, buffer.ToHexString("X"), "encode upper"); + } + + [TestMethod] + public void Decode() + { + var buffer = Enumerable.Range(byte.MinValue, byte.MaxValue).Select(b => (byte) b).ToArray(); + var lowerHex = string.Concat(buffer.Select(b => b.ToString("x2")).ToArray()); + var upperHex = string.Concat(buffer.Select(b => b.ToString("X2")).ToArray()); + + CollectionAssert.AreEqual(buffer, lowerHex.ToHexBuffer(), "decode lower"); + CollectionAssert.AreEqual(buffer, upperHex.ToHexBuffer(), "decode upper"); + } + + [TestMethod] + public void InvalidFormatSpecifier() + { + ExceptionAssert.Throws(() => HexString.Encode(new byte[0], "...")); + } + + [TestMethod] + public void InvalidHexStrings() + { + ExceptionAssert.Throws(() => HexString.Decode("0")); + ExceptionAssert.Throws(() => HexString.Decode("0Z")); + } + } +} diff --git a/src/MultiFormats.Tests/MultBaseTest.cs b/src/MultiFormats.Tests/MultBaseTest.cs new file mode 100644 index 0000000000..014a14689d --- /dev/null +++ b/src/MultiFormats.Tests/MultBaseTest.cs @@ -0,0 +1,274 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Text; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats.Registry; + +namespace MultiFormats.Tests +{ + [TestClass] + public class MultiBaseTest + { + [TestMethod] + public void Codec() + { + var bytes = new byte[] + { + 1, 2, 3, 4, 5 + }; + var bytes1 = MultiBase.Decode(MultiBase.Encode(bytes)); + var bytes2 = MultiBase.Decode(MultiBase.Encode(bytes, "base16")); + CollectionAssert.AreEqual(bytes, bytes1); + CollectionAssert.AreEqual(bytes, bytes2); + } + + [TestMethod] + public void Encode_Unknown_Algorithm() + { + var bytes = new byte[] + { + 1, 2, 3, 4, 5 + }; + ExceptionAssert.Throws(() => MultiBase.Encode(bytes, "unknown")); + } + + [TestMethod] + public void Encode_Null_Data_Not_Allowed() + { + ExceptionAssert.Throws(() => MultiBase.Encode(null)); + } + + [TestMethod] + public void Decode_Bad_Formats() + { + ExceptionAssert.Throws(() => MultiBase.Decode(null)); + ExceptionAssert.Throws(() => MultiBase.Decode("")); + ExceptionAssert.Throws(() => MultiBase.Decode(" ")); + + ExceptionAssert.Throws(() => MultiBase.Decode("?")); + ExceptionAssert.Throws(() => MultiBase.Decode("??")); + ExceptionAssert.Throws(() => MultiBase.Decode("???")); + ExceptionAssert.Throws(() => MultiBase.Decode("fXX")); + } + + private sealed class TestVector + { + public string Algorithm { get; set; } + public string Input { get; set; } + public string Output { get; set; } + } + + private TestVector[] TestVectors = + { + new TestVector + { + Algorithm = "base16", + Input = "yes mani !", + Output = "f796573206d616e692021" + }, + new TestVector + { + Algorithm = "base32", + Input = "yes mani !", + Output = "bpfsxgidnmfxgsibb" + }, + new TestVector + { + Algorithm = "base32pad", + Input = "yes mani !", + Output = "cpfsxgidnmfxgsibb" + }, + new TestVector + { + Algorithm = "base32", + Input = "f", + Output = "bmy" + }, + new TestVector + { + Algorithm = "base32pad", + Input = "f", + Output = "cmy======" + }, + new TestVector + { + Algorithm = "base32hex", + Input = "f", + Output = "vco" + }, + new TestVector + { + Algorithm = "base32hexpad", + Input = "f", + Output = "tco======" + }, + new TestVector + { + Algorithm = "base64pad", + Input = "f", + Output = "MZg==" + }, + new TestVector + { + Algorithm = "base64", + Input = "f", + Output = "mZg" + }, + new TestVector + { + Algorithm = "base64", + Input = "\u00f7\u00ef\u00ff", + Output = "mw7fDr8O/" + }, + new TestVector + { + Algorithm = "base64url", + Input = "\u00f7\u00ef\u00ff", + Output = "uw7fDr8O_" + }, + new TestVector + { + Algorithm = "base64url", + Input = "f", + Output = "uZg" + }, + new TestVector + { + Algorithm = "base64url", + Input = "fo", + Output = "uZm8" + }, + new TestVector + { + Algorithm = "base64url", + Input = "foo", + Output = "uZm9v" + }, + new TestVector + { + Algorithm = "BASE16", + Input = "yes mani !", + Output = "F796573206D616E692021" + }, + new TestVector + { + Algorithm = "BASE32", + Input = "yes mani !", + Output = "BPFSXGIDNMFXGSIBB" + }, + new TestVector + { + Algorithm = "BASE32PAD", + Input = "yes mani !", + Output = "CPFSXGIDNMFXGSIBB" + }, + new TestVector + { + Algorithm = "BASE32", + Input = "f", + Output = "BMY" + }, + new TestVector + { + Algorithm = "BASE32PAD", + Input = "f", + Output = "CMY======" + }, + new TestVector + { + Algorithm = "BASE32HEX", + Input = "f", + Output = "VCO" + }, + new TestVector + { + Algorithm = "BASE32HEXPAD", + Input = "f", + Output = "TCO======" + }, + new TestVector + { + Algorithm = "base32z", + Input = "Decentralize everything!!", + Output = "het1sg3mqqt3gn5djxj11y3msci3817depfzgqejb" + }, + new TestVector + { + Algorithm = "base32z", + Input = "yes mani !", + Output = "hxf1zgedpcfzg1ebb" + }, + new TestVector + { + Algorithm = "base32z", + Input = "hello world", + Output = "hpb1sa5dxrb5s6hucco" + }, + new TestVector + { + Algorithm = "base32z", + Input = "\x00\x00yes mani !", + Output = "hyyy813murbssn5ujryoo" + }, + }; + + /// + /// Test vectors from various sources. + /// + [TestMethod] + public void CheckMultiBase() + { + foreach (var v in TestVectors) + { + var bytes = Encoding.UTF8.GetBytes(v.Input); + var s = MultiBase.Encode(bytes, v.Algorithm); + Assert.AreEqual(v.Output, s); + CollectionAssert.AreEqual(bytes, MultiBase.Decode(s)); + } + } + + [TestMethod] + public void EmptyData() + { + var empty = new byte[0]; + foreach (var alg in MultiBaseAlgorithm.All) + { + var s = MultiBase.Encode(empty, alg.Name); + CollectionAssert.AreEqual(empty, MultiBase.Decode(s), alg.Name); + } + } + + [TestMethod] + public void Invalid_Encoded_String() + { + foreach (var alg in MultiBaseAlgorithm.All) + { + var bad = alg.Code + "?"; + ExceptionAssert.Throws(() => MultiBase.Decode(bad)); + } + } + } +} diff --git a/src/MultiFormats.Tests/MultiAddressTest.cs b/src/MultiFormats.Tests/MultiAddressTest.cs new file mode 100644 index 0000000000..6de56632b6 --- /dev/null +++ b/src/MultiFormats.Tests/MultiAddressTest.cs @@ -0,0 +1,437 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Net; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Newtonsoft.Json; + +namespace MultiFormats.Tests +{ + [TestClass] + public class MultiAddressTest + { + private const string Somewhere = + "/ip4/10.1.10.10/tcp/29087/ipfs/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC"; + + private const string Nowhere = "/ip4/10.1.10.11/tcp/29087/ipfs/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC"; + + [TestMethod] + public void Parsing() + { + var a = new MultiAddress(Somewhere); + Assert.AreEqual(3, a.Protocols.Count); + Assert.AreEqual("ip4", a.Protocols[0].Name); + Assert.AreEqual("10.1.10.10", a.Protocols[0].Value); + Assert.AreEqual("tcp", a.Protocols[1].Name); + Assert.AreEqual("29087", a.Protocols[1].Value); + Assert.AreEqual("ipfs", a.Protocols[2].Name); + Assert.AreEqual("QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC", a.Protocols[2].Value); + + Assert.AreEqual(0, new MultiAddress((string) null).Protocols.Count); + Assert.AreEqual(0, new MultiAddress("").Protocols.Count); + Assert.AreEqual(0, new MultiAddress(" ").Protocols.Count); + } + + [TestMethod] + public void Unknown_Protocol_Name() + { + ExceptionAssert.Throws(() => new MultiAddress("/foobar/123")); + } + + [TestMethod] + public void Missing_Protocol_Name() { ExceptionAssert.Throws(() => new MultiAddress("/")); } + + [TestMethod] + public new void ToString() { Assert.AreEqual(Somewhere, new MultiAddress(Somewhere).ToString()); } + + [TestMethod] + public void Value_Equality() + { + var a0 = new MultiAddress(Somewhere); + var a1 = new MultiAddress(Somewhere); + var b = new MultiAddress(Nowhere); + MultiAddress c = null; + MultiAddress d = null; + + Assert.IsTrue(c == d); + Assert.IsFalse(c == b); + Assert.IsFalse(b == c); + + Assert.IsFalse(c != d); + Assert.IsTrue(c != b); + Assert.IsTrue(b != c); + +#pragma warning disable 1718 + Assert.IsTrue(a0 == a0); + Assert.IsTrue(a0 == a1); + Assert.IsFalse(a0 == b); + +#pragma warning disable 1718 + Assert.IsFalse(a0 != a0); + Assert.IsFalse(a0 != a1); + Assert.IsTrue(a0 != b); + + Assert.IsTrue(a0.Equals(a0)); + Assert.IsTrue(a0.Equals(a1)); + Assert.IsFalse(a0.Equals(b)); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0.GetHashCode(), a0.GetHashCode()); + Assert.AreEqual(a0.GetHashCode(), a1.GetHashCode()); + Assert.AreNotEqual(a0.GetHashCode(), b.GetHashCode()); + } + + [TestMethod] + public void Bad_Port() + { + var tcp = new MultiAddress("/tcp/65535"); + ExceptionAssert.Throws(() => new MultiAddress("/tcp/x")); + ExceptionAssert.Throws(() => new MultiAddress("/tcp/65536")); + + var udp = new MultiAddress("/udp/65535"); + ExceptionAssert.Throws(() => new MultiAddress("/upd/x")); + ExceptionAssert.Throws(() => new MultiAddress("/udp/65536")); + } + + [TestMethod] + public void Bad_IPAddress() + { + var ipv4 = new MultiAddress("/ip4/127.0.0.1"); + ExceptionAssert.Throws(() => new MultiAddress("/ip4/x")); + ExceptionAssert.Throws(() => new MultiAddress("/ip4/127.")); + ExceptionAssert.Throws(() => new MultiAddress("/ip4/::1")); + + var ipv6 = new MultiAddress("/ip6/::1"); + ExceptionAssert.Throws(() => new MultiAddress("/ip6/x")); + ExceptionAssert.Throws(() => new MultiAddress("/ip6/03:")); + ExceptionAssert.Throws(() => new MultiAddress("/ip6/127.0.0.1")); + } + + [TestMethod] + public void Bad_Onion_MultiAdress() + { + var badCases = new[] + { + "/onion/9imaq4ygg2iegci7:80", + "/onion/aaimaq4ygg2iegci7:80", + "/onion/timaq4ygg2iegci7:0", + "/onion/timaq4ygg2iegci7:-1", + "/onion/timaq4ygg2iegci7", + "/onion/timaq4ygg2iegci@:666", + }; + foreach (var badCase in badCases) + { + ExceptionAssert.Throws(() => new MultiAddress(badCase)); + } + } + + [TestMethod] + public void RoundTripping() + { + var addresses = new[] + { + Somewhere, + "/ip4/1.2.3.4/tcp/80/http", + "/ip6/3ffe:1900:4545:3:200:f8ff:fe21:67cf/tcp/443/https", + "/ip6/3ffe:1900:4545:3:200:f8ff:fe21:67cf/udp/8001", + "/ip6/3ffe:1900:4545:3:200:f8ff:fe21:67cf/sctp/8001", + "/ip6/3ffe:1900:4545:3:200:f8ff:fe21:67cf/dccp/8001", + "/ip4/1.2.3.4/tcp/80/ws", + "/libp2p-webrtc-star/ip4/127.0.0.1/tcp/9090/ws/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", + "/ip4/127.0.0.1/tcp/1234/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", + "/ip4/1.2.3.4/tcp/80/udt", + "/ip4/1.2.3.4/tcp/80/utp", + "/onion/aaimaq4ygg2iegci:80", + "/onion/timaq4ygg2iegci7:80/http", + "/p2p-circuit/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC", + "/dns/ipfs.io", + "/dns4/ipfs.io", + "/dns6/ipfs.io", + "/dns4/wss0.bootstrap.libp2p.io/tcp/443/wss/ipfs/QmZMxNdpMkewiVZLMRxaNxUeZpDUb34pWjZ1kZvsd16Zic", + "/ip4/127.0.0.0/ipcidr/16", + "/p2p/QmNnooDu7bfjPFoTZYxMNLWUQJyrVwtbZg5gBMjTezGAJN", + "/ip4/127.0.0.1/udp/4023/quic", + }; + foreach (var a in addresses) + { + var ma0 = new MultiAddress(a); + + var ms = new MemoryStream(); + ma0.Write(ms); + ms.Position = 0; + var ma1 = new MultiAddress(ms); + Assert.AreEqual(ma0, ma1); + + var ma2 = new MultiAddress(ma0.ToString()); + Assert.AreEqual(ma0, ma2); + + var ma3 = new MultiAddress(ma0.ToArray()); + Assert.AreEqual(ma0, ma3); + } + } + + [TestMethod] + public void Reading_Invalid_Code() + { + ExceptionAssert.Throws(() => new MultiAddress(new byte[] + { + 0x7F + })); + } + + [TestMethod] + public void Reading_Invalid_Text() + { + ExceptionAssert.Throws(() => new MultiAddress("tcp/80")); + } + + [TestMethod] + public void Implicit_Conversion_From_String() + { + MultiAddress a = Somewhere; + Assert.IsInstanceOfType(a, typeof(MultiAddress)); + } + + [TestMethod] + public void Wire_Formats() + { + Assert.AreEqual( + new MultiAddress("/ip4/127.0.0.1/udp/1234").ToArray().ToHexString(), + "047f000001910204d2"); + Assert.AreEqual( + new MultiAddress("/ip4/127.0.0.1/udp/1234/ip4/127.0.0.1/tcp/4321").ToArray().ToHexString(), + "047f000001910204d2047f0000010610e1"); + Assert.AreEqual( + new MultiAddress("/ip6/2001:8a0:7ac5:4201:3ac9:86ff:fe31:7095").ToArray().ToHexString(), + "29200108a07ac542013ac986fffe317095"); + Assert.AreEqual( + new MultiAddress("/ipfs/QmcgpsyWgH8Y8ajJz1Cu72KnS5uo2Aa2LpzU7kinSupNKC").ToArray().ToHexString(), + "a503221220d52ebb89d85b02a284948203a62ff28389c57c9f42beec4ec20db76a68911c0b"); + Assert.AreEqual( + new MultiAddress("/ip4/127.0.0.1/udp/1234/utp").ToArray().ToHexString(), + "047f000001910204d2ae02"); + Assert.AreEqual( + new MultiAddress("/onion/aaimaq4ygg2iegci:80").ToArray().ToHexString(), + "bc030010c0439831b48218480050"); + } + + [TestMethod] + public void PeerID_With_ipfs() + { + var ma = new MultiAddress("/ip4/10.1.10.10/tcp/29087/ipfs/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC"); + Assert.AreEqual("QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC", ma.PeerId.ToBase58()); + } + + [TestMethod] + public void PeerID_With_p2p() + { + var ma = new MultiAddress("/ip4/10.1.10.10/tcp/29087/p2p/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC"); + Assert.AreEqual("QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC", ma.PeerId.ToBase58()); + } + + [TestMethod] + public void PeerID_ipfs_p2p_are_equal() + { + var ipfs = new MultiAddress( + "/ip4/10.1.10.10/tcp/29087/ipfs/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC"); + var p2P = new MultiAddress("/ip4/10.1.10.10/tcp/29087/p2p/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC"); + Assert.AreEqual(ipfs, p2P); + + var p2P1 = new MultiAddress("/ip4/10.1.10.10/tcp/29087/p2p/QmVCSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC"); + Assert.AreNotEqual(p2P, p2P1); + + var p2P2 = new MultiAddress("/p2p/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC"); + Assert.AreNotEqual(p2P, p2P2); + } + + [TestMethod] + public void PeerID_Missing() + { + var ma = new MultiAddress("/ip4/10.1.10.10/tcp/29087"); + ExceptionAssert.Throws(() => + { + var _ = ma.PeerId; + }); + } + + [TestMethod] + public void PeerId_IsPresent() + { + Assert.IsTrue( + new MultiAddress("/ip4/10.1.10.10/tcp/29087/ipfs/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC") + .HasPeerId); + Assert.IsTrue( + new MultiAddress("/ip4/10.1.10.10/tcp/29087/p2p/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC") + .HasPeerId); + Assert.IsFalse(new MultiAddress("/ip4/10.1.10.10/tcp/29087").HasPeerId); + } + + [TestMethod] + public void Cloning() + { + var ma1 = new MultiAddress("/ip4/10.1.10.10/tcp/29087"); + var ma2 = ma1.Clone(); + Assert.AreEqual(ma1, ma2); + Assert.AreNotSame(ma1, ma2); + Assert.AreNotSame(ma1.Protocols, ma2.Protocols); + for (var i = 0; i < ma1.Protocols.Count; ++i) + { + var p1 = ma1.Protocols[i]; + var p2 = ma2.Protocols[i]; + Assert.AreEqual(p1.Code, p2.Code); + Assert.AreEqual(p1.Name, p2.Name); + Assert.AreEqual(p1.Value, p2.Value); + Assert.AreNotSame(p1, p2); + } + } + + [TestMethod] + public void Ipv6ScopeId_Ignored() + { + var ma1 = new MultiAddress("/ip6/fe80::7573:b0a8:46b0:0bad%17/tcp/4009"); + var ma2 = new MultiAddress("/ip6/fe80::7573:b0a8:46b0:0bad/tcp/4009"); + Assert.AreEqual(ma2, ma1); + Assert.AreEqual(ma2.ToString(), ma1.ToString()); + } + + [TestMethod] + public void TryCreate_FromString() + { + Assert.IsNotNull(MultiAddress.TryCreate("/ip4/1.2.3.4/tcp/80")); + Assert.IsNull(MultiAddress.TryCreate("/tcp/alpha")); // bad port + Assert.IsNull(MultiAddress.TryCreate("/foobar")); // bad protocol + } + + [TestMethod] + public void TryCreate_FromBytes() + { + var good = MultiAddress.TryCreate("/ip4/1.2.3.4/tcp/80"); + var good1 = MultiAddress.TryCreate(good.ToArray()); + Assert.AreEqual(good, good1); + + Assert.IsNull(MultiAddress.TryCreate(new byte[] + { + 0x7f + })); + } + + [TestMethod] + public void JsonSerialization() + { + var a = new MultiAddress("/ip6/fe80::7573:b0a8:46b0:0bad/tcp/4009"); + var json = JsonConvert.SerializeObject(a); + Assert.AreEqual($"\"{a}\"", json); + var b = JsonConvert.DeserializeObject(json); + Assert.AreEqual(a.ToString(), b.ToString()); + + json = JsonConvert.SerializeObject(null); + b = JsonConvert.DeserializeObject(json); + Assert.IsNull(b); + } + + [TestMethod] + public void WithPeerId() + { + const string id = "QmQusTXc1Z9C1mzxsqC9ZTFXCgSkpBRGgW4Jk2QYHxKE22"; + const string id3 = "QmQusTXc1Z9C1mzxsqC9ZTFXCgSkpBRGgW4Jk2QYHxKE33"; + + var ma1 = new MultiAddress("/ip4/127.0.0.1/tcp/4001"); + Assert.AreEqual($"{ma1}/p2p/{id}", ma1.WithPeerId(id)); + + ma1 = new MultiAddress($"/ip4/127.0.0.1/tcp/4001/ipfs/{id}"); + Assert.AreSame(ma1, ma1.WithPeerId(id)); + + ma1 = new MultiAddress($"/ip4/127.0.0.1/tcp/4001/p2p/{id}"); + Assert.AreSame(ma1, ma1.WithPeerId(id)); + + ExceptionAssert.Throws(() => + { + ma1 = new MultiAddress($"/ip4/127.0.0.1/tcp/4001/ipfs/{id3}"); + Assert.AreSame(ma1, ma1.WithPeerId(id)); + }); + } + + [TestMethod] + public void WithoutPeerId() + { + var id = "QmQusTXc1Z9C1mzxsqC9ZTFXCgSkpBRGgW4Jk2QYHxKE22"; + + var ma1 = new MultiAddress("/ip4/127.0.0.1/tcp/4001"); + Assert.AreSame(ma1, ma1.WithoutPeerId()); + + ma1 = new MultiAddress($"/ip4/127.0.0.1/tcp/4001/ipfs/{id}"); + Assert.AreEqual("/ip4/127.0.0.1/tcp/4001", ma1.WithoutPeerId()); + + ma1 = new MultiAddress($"/ip4/127.0.0.1/tcp/4001/p2p/{id}"); + Assert.AreEqual("/ip4/127.0.0.1/tcp/4001", ma1.WithoutPeerId()); + } + + [TestMethod] + public void Alias_Equality() + { + var a = new MultiAddress("/ipfs/QmQusTXc1Z9C1mzxsqC9ZTFXCgSkpBRGgW4Jk2QYHxKE22"); + var b = new MultiAddress("/p2p/QmQusTXc1Z9C1mzxsqC9ZTFXCgSkpBRGgW4Jk2QYHxKE22"); + + Assert.AreEqual(a, b); + Assert.IsTrue(a == b); + Assert.AreEqual(a.GetHashCode(), b.GetHashCode()); + } + + [TestMethod] + public void GetHashCode_NullValue() + { + var a = new MultiAddress( + "/ip4/139.178.69.3/udp/4001/quic/p2p/QmdGQoGuK3pao6bRDqGSDvux5SFHa4kC2XNFfHFcvcbydY/p2p-circuit/ipfs/QmPJkpfUedzahgVAj6tTUa3DHKVkfTSyvUmnn1USFpiCaF"); + var _ = a.GetHashCode(); + } + + [TestMethod] + public void FromIpAddress() + { + var ma = new MultiAddress(IPAddress.Loopback); + Assert.AreEqual("/ip4/127.0.0.1", ma.ToString()); + + ma = new MultiAddress(IPAddress.IPv6Loopback); + Assert.AreEqual("/ip6/::1", ma.ToString()); + } + + [TestMethod] + public void FromIpEndpoint() + { + var ma = new MultiAddress(new IPEndPoint(IPAddress.Loopback, 4001)); + Assert.AreEqual("/ip4/127.0.0.1/tcp/4001", ma.ToString()); + + ma = new MultiAddress(new IPEndPoint(IPAddress.IPv6Loopback, 4002)); + Assert.AreEqual("/ip6/::1/tcp/4002", ma.ToString()); + } + } +} diff --git a/src/MultiFormats.Tests/MultiFormats.Tests.csproj b/src/MultiFormats.Tests/MultiFormats.Tests.csproj new file mode 100644 index 0000000000..4185d6914e --- /dev/null +++ b/src/MultiFormats.Tests/MultiFormats.Tests.csproj @@ -0,0 +1,26 @@ + + + + net6.0 + + + + + + false + opencover + + + + + + + + + + + + + + + diff --git a/src/MultiFormats.Tests/MultiHashTest.cs b/src/MultiFormats.Tests/MultiHashTest.cs new file mode 100644 index 0000000000..66330b1b1b --- /dev/null +++ b/src/MultiFormats.Tests/MultiHashTest.cs @@ -0,0 +1,558 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Text; +using Google.Protobuf; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats.Registry; +using Newtonsoft.Json; + +namespace MultiFormats.Tests +{ + [TestClass] + public sealed class MultiHashTest + { + [TestMethod] + public void HashNames() + { + var _ = new MultiHash("sha1", new byte[20]); + _ = new MultiHash("sha2-256", new byte[32]); + _ = new MultiHash("sha2-512", new byte[64]); + _ = new MultiHash("keccak-512", new byte[64]); + } + + [TestMethod] + public void Unknown_Hash_Name() + { + ExceptionAssert.Throws(() => new MultiHash(null, new byte[0])); + ExceptionAssert.Throws(() => new MultiHash("", new byte[0])); + ExceptionAssert.Throws(() => new MultiHash("md5", new byte[0])); + } + + [TestMethod] + public void Write_Null_Stream() + { + var mh = new MultiHash("QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB"); + ExceptionAssert.Throws(() => mh.Write((CodedOutputStream) null)); + } + + [TestMethod] + public void Parsing_Unknown_Hash_Number() + { + HashingAlgorithm unknown = null; + EventHandler unknownHandler = (s, e) => { unknown = e.Algorithm; }; + var ms = new MemoryStream(new byte[] + { + 0x01, 0x02, 0x0a, 0x0b + }); + MultiHash.UnknownHashingAlgorithm += unknownHandler; + try + { + var mh = new MultiHash(ms); + Assert.AreEqual("ipfs-1", mh.Algorithm.Name); + Assert.AreEqual("ipfs-1", mh.Algorithm.ToString()); + Assert.AreEqual(1, mh.Algorithm.Code); + Assert.AreEqual(2, mh.Algorithm.DigestSize); + Assert.AreEqual(0xa, mh.Digest[0]); + Assert.AreEqual(0xb, mh.Digest[1]); + Assert.IsNotNull(unknown, "unknown handler not called"); + Assert.AreEqual("ipfs-1", unknown.Name); + Assert.AreEqual(1, unknown.Code); + Assert.AreEqual(2, unknown.DigestSize); + } + finally + { + // ReSharper disable once DelegateSubtraction + MultiHash.UnknownHashingAlgorithm -= unknownHandler; + } + } + + [TestMethod] + public void Parsing_Wrong_Digest_Size() + { + var ms = new MemoryStream(new byte[] + { + 0x11, 0x02, 0x0a, 0x0b + }); + ExceptionAssert.Throws(() => new MultiHash(ms)); + } + + [TestMethod] + public void Invalid_Digest() + { + ExceptionAssert.Throws(() => new MultiHash("sha1", null)); + ExceptionAssert.Throws(() => new MultiHash("sha1", new byte[0])); + ExceptionAssert.Throws(() => new MultiHash("sha1", new byte[21])); + } + + [TestMethod] + public void Base58_Encode_Decode() + { + var mh = new MultiHash("QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB"); + Assert.AreEqual("sha2-256", mh.Algorithm.Name); + Assert.AreEqual(32, mh.Digest.Length); + Assert.AreEqual("QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB", mh.ToBase58()); + } + + [TestMethod] + public void Base32_Encode() + { + var mh = new MultiHash("QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB"); + Assert.AreEqual("sha2-256", mh.Algorithm.Name); + Assert.AreEqual(32, mh.Digest.Length); + Assert.AreEqual("ciqbed3k6ya5i3qqwljochwxdrk5exzqilbckapedujenz5b5hj5r3a", mh.ToBase32()); + } + + [TestMethod] + public void Compute_Hash_Array() + { + var hello = Encoding.UTF8.GetBytes("Hello, world."); + var mh = MultiHash.ComputeHash(hello); + Assert.AreEqual(MultiHash.DefaultAlgorithmName, mh.Algorithm.Name); + Assert.IsNotNull(mh.Digest); + } + + [TestMethod] + public void Compute_Hash_Stream() + { + var hello = new MemoryStream(Encoding.UTF8.GetBytes("Hello, world.")); + hello.Position = 0; + var mh = MultiHash.ComputeHash(hello); + Assert.AreEqual(MultiHash.DefaultAlgorithmName, mh.Algorithm.Name); + Assert.IsNotNull(mh.Digest); + } + + [TestMethod] + public void Compute_Not_Implemented_Hash_Array() + { + var alg = HashingAlgorithm.Register("not-implemented", 0x0F, 32); + try + { + var hello = Encoding.UTF8.GetBytes("Hello, world."); + ExceptionAssert.Throws(() => MultiHash.ComputeHash(hello, "not-implemented")); + } + finally + { + HashingAlgorithm.DeRegister(alg); + } + } + + [TestMethod] + public void Matches_Array() + { + var hello = Encoding.UTF8.GetBytes("Hello, world."); + var hello1 = Encoding.UTF8.GetBytes("Hello, world"); + var mh = MultiHash.ComputeHash(hello); + Assert.IsTrue(mh.Matches(hello)); + Assert.IsFalse(mh.Matches(hello1)); + + var mh1 = MultiHash.ComputeHash(hello, "sha1"); + Assert.IsTrue(mh1.Matches(hello)); + Assert.IsFalse(mh1.Matches(hello1)); + + var mh2 = MultiHash.ComputeHash(hello, "sha2-512"); + Assert.IsTrue(mh2.Matches(hello)); + Assert.IsFalse(mh2.Matches(hello1)); + + var mh3 = MultiHash.ComputeHash(hello, "keccak-512"); + Assert.IsTrue(mh3.Matches(hello)); + Assert.IsFalse(mh3.Matches(hello1)); + } + + [TestMethod] + public void Matches_Stream() + { + var hello = new MemoryStream(Encoding.UTF8.GetBytes("Hello, world.")); + var hello1 = new MemoryStream(Encoding.UTF8.GetBytes("Hello, world")); + hello.Position = 0; + var mh = MultiHash.ComputeHash(hello); + + hello.Position = 0; + Assert.IsTrue(mh.Matches(hello)); + + hello1.Position = 0; + Assert.IsFalse(mh.Matches(hello1)); + } + + [TestMethod] + public void Wire_Formats() + { + var hashes = new[] + { + "5drNu81uhrFLRiS4bxWgAkpydaLUPW", // sha1 + "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4", // sha2_256 + "8Vtkv2tdQ43bNGdWN9vNx9GVS9wrbXHk4ZW8kmucPmaYJwwedXir52kti9wJhcik4HehyqgLrQ1hBuirviLhxgRBNv", // sha2_512 + }; + var helloWorld = Encoding.UTF8.GetBytes("hello world"); + foreach (var hash in hashes) + { + var mh = new MultiHash(hash); + Assert.IsTrue(mh.Matches(helloWorld), hash); + } + } + + [TestMethod] + public void To_String_Is_Base58_Representation() + { + var hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"; + var mh = new MultiHash(hash); + Assert.AreEqual(hash, mh.ToString()); + } + + [TestMethod] + public void Implicit_Conversion_From_String() + { + var hash = "QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"; + MultiHash mh = hash; + Assert.IsNotNull(mh); + Assert.IsInstanceOfType(mh, typeof(MultiHash)); + Assert.AreEqual(hash, mh.ToString()); + } + + [TestMethod] + public void Value_Equality() + { + var a0 = new MultiHash("QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"); + var a1 = new MultiHash("QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L4"); + var b = new MultiHash("QmaozNR7DZHQK1ZcU9p7QdrshMvXqWK6gpu5rmrkPdT3L5"); + MultiHash c = null; + MultiHash d = null; + + Assert.IsTrue(c == d); + Assert.IsFalse(c == b); + Assert.IsFalse(b == c); + + Assert.IsFalse(c != d); + Assert.IsTrue(c != b); + Assert.IsTrue(b != c); + +#pragma warning disable 1718 + Assert.IsTrue(a0 == a0); + Assert.IsTrue(a0 == a1); + Assert.IsFalse(a0 == b); + +#pragma warning disable 1718 + Assert.IsFalse(a0 != a0); + Assert.IsFalse(a0 != a1); + Assert.IsTrue(a0 != b); + + Assert.IsTrue(a0.Equals(a0)); + Assert.IsTrue(a0.Equals(a1)); + Assert.IsFalse(a0.Equals(b)); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0, a0); + Assert.AreEqual(a0, a1); + Assert.AreNotEqual(a0, b); + + Assert.AreEqual(a0.GetHashCode(), a0.GetHashCode()); + Assert.AreEqual(a0.GetHashCode(), a1.GetHashCode()); + Assert.AreNotEqual(a0.GetHashCode(), b.GetHashCode()); + } + + [TestMethod] + public void Varint_Hash_Code_and_Length() + { + var concise = "1220f8c3bf62a9aa3e6fc1619c250e48abe7519373d3edf41be62eb5dc45199af2ef" + .ToHexBuffer(); + var mh = new MultiHash(new MemoryStream(concise, false)); + Assert.AreEqual("sha2-256", mh.Algorithm.Name); + Assert.AreEqual(0x12, mh.Algorithm.Code); + Assert.AreEqual(0x20, mh.Algorithm.DigestSize); + + var longer = "9200a000f8c3bf62a9aa3e6fc1619c250e48abe7519373d3edf41be62eb5dc45199af2ef" + .ToHexBuffer(); + mh = new MultiHash(new MemoryStream(longer, false)); + Assert.AreEqual("sha2-256", mh.Algorithm.Name); + Assert.AreEqual(0x12, mh.Algorithm.Code); + Assert.AreEqual(0x20, mh.Algorithm.DigestSize); + } + + [TestMethod] + public void Compute_Hash_All_Algorithms() + { + foreach (var alg in HashingAlgorithm.All) + try + { + var mh = MultiHash.ComputeHash(new byte[0], alg.Name); + Assert.IsNotNull(mh, alg.Name); + Assert.AreEqual(alg.Code, mh.Algorithm.Code, alg.Name); + Assert.AreEqual(alg.Name, mh.Algorithm.Name, alg.Name); + Assert.AreEqual(alg.DigestSize, mh.Algorithm.DigestSize, alg.Name); + Assert.AreEqual(alg.DigestSize, mh.Digest.Length, alg.Name); + } + catch (NotImplementedException) + { + // If NYI then can't test it. + } + } + + [TestMethod] + public void Example() + { + var hello = Encoding.UTF8.GetBytes("Hello world"); + var mh = MultiHash.ComputeHash(hello); + Console.WriteLine($"| hash code | 0x{mh.Algorithm.Code.ToString("x")} |"); + Console.WriteLine($"| digest length | 0x{mh.Digest.Length.ToString("x")} |"); + Console.WriteLine($"| digest value | {mh.Digest.ToHexString()} |"); + Console.WriteLine($"| binary | {mh.ToArray().ToHexString()} |"); + Console.WriteLine($"| base 58 | {mh.ToBase58()} |"); + Console.WriteLine($"| base 32 | {mh.ToBase32()} |"); + } + + private sealed class TestVector + { + public string Algorithm { get; set; } + public string Input { get; set; } + public string Output { get; set; } + public bool Ignore { get; set; } + } + + private TestVector[] TestVectors = + { + // From https://github.com/multiformats/js-multihashing-async/blob/master/test/fixtures/encodes.js + new TestVector + { + Algorithm = "sha1", + Input = "beep boop", + Output = "11147c8357577f51d4f0a8d393aa1aaafb28863d9421" + }, + new TestVector + { + Algorithm = "sha2-256", + Input = "beep boop", + Output = "122090ea688e275d580567325032492b597bc77221c62493e76330b85ddda191ef7c" + }, + new TestVector + { + Algorithm = "sha2-512", + Input = "beep boop", + Output = + "134014f301f31be243f34c5668937883771fa381002f1aaa5f31b3f78e500b66ff2f4f8ea5e3c9f5a61bd073e2452c480484b02e030fb239315a2577f7ae156af177" + }, + new TestVector + { + Algorithm = "sha3-512", + Input = "beep boop", + Output = + "1440fae2c9eb19906057f8bf507f0e73ee02bb669d58c3069e7718b89ca4d314cf4fd6f1679019cc46d185c7af34f6c05a307b070e74e9ed5b9c64f86aacc2b90d10" + }, + new TestVector + { + Algorithm = "sha3-384", + Input = "beep boop", + Output = + "153075a9cff1bcfbe8a7025aa225dd558fb002769d4bf3b67d2aaf180459172208bea989804aefccf060b583e629e5f41e8d" + }, + new TestVector + { + Algorithm = "sha3-256", + Input = "beep boop", + Output = "1620828705da60284b39de02e3599d1f39e6c1df001f5dbf63c9ec2d2c91a95a427f" + }, + new TestVector + { + Algorithm = "sha3-224", + Input = "beep boop", + Output = "171c0da73a89549018df311c0a63250e008f7be357f93ba4e582aaea32b8" + }, + new TestVector + { + Algorithm = "shake-128", + Input = "beep boop", + Output = "18105fe422311f770743c2e0d86bcca09211" + }, + new TestVector + { + Algorithm = "shake-256", + Input = "beep boop", + Output = "192059feb5565e4f924baef74708649fed376d63948a862322ed763ecf093b63b38b" + }, + new TestVector + { + Algorithm = "keccak-224", + Input = "beep boop", + Output = "1a1c2bd72cde2f75e523512999eb7639f17b699efe29bec342f5a0270896" + }, + new TestVector + { + Algorithm = "keccak-256", + Input = "beep boop", + Output = "1b20ee6f6b4ce5d754c01203cb3b99294b88615df5999e20d6fe509204fa254a0f97" + }, + new TestVector + { + Algorithm = "keccak-384", + Input = "beep boop", + Output = + "1c300e2fcca40e861fc425a2503a65f4a4befab7be7f193e57654ca3713e85262b035e54d5ade93f9632b810ab88b04f7d84" + }, + new TestVector + { + Algorithm = "keccak-512", + Input = "beep boop", + Output = + "1d40e161c54798f78eba3404ac5e7e12d27555b7b810e7fd0db3f25ffa0c785c438331b0fbb6156215f69edf403c642e5280f4521da9bd767296ec81f05100852e78" + }, + new TestVector + { + Algorithm = "blake2b-512", + Input = "beep boop", + Output = + "c0e402400eac6255ba822373a0948122b8d295008419a8ab27842ee0d70eca39855621463c03ec75ac3610aacfdff89fa989d8d61fc00450148f289eb5b12ad1a954f659" + }, + new TestVector + { + Algorithm = "blake2b-160", + Input = "beep boop", + Output = "94e40214fe303247293e54e0a7ea48f9408ca68b36b08442" + }, + new TestVector + { + Algorithm = "blake2s-256", + Input = "beep boop", + Output = "e0e402204542eaca484e4311def8af74b546edd7fceb49eeb3cdcfd8a4a72ed0dc81d4c0" + }, + new TestVector + { + Algorithm = "dbl-sha2-256", + Input = "beep boop", + Output = "56209cd9115d76945c2455b1450295b05f4edeba2e7286bc24c23e266b48faf578c0" + }, + new TestVector + { + Algorithm = "identity", + Input = "ab", + Output = "00026162" + }, + new TestVector + { + Algorithm = "id", + Input = "ab", + Output = "00026162" + }, + }; + + [TestMethod] + public void CheckMultiHash() + { + foreach (var v in TestVectors) + { + if (v.Ignore) continue; + var bytes = Encoding.UTF8.GetBytes(v.Input); + var mh = MultiHash.ComputeHash(bytes, v.Algorithm); + Assert.AreEqual(v.Output, mh.ToArray().ToHexString(), v.Algorithm); + } + } + + [TestMethod] + public void CheckMultiHash_Stream() + { + foreach (var v in TestVectors) + { + if (v.Ignore) continue; + var bytes = Encoding.UTF8.GetBytes(v.Input); + using (var ms = new MemoryStream(bytes, false)) + { + var mh = MultiHash.ComputeHash(ms, v.Algorithm); + Assert.AreEqual(v.Output, mh.ToArray().ToHexString(), v.Algorithm); + } + } + } + + [TestMethod] + public void IdentityHash() + { + var hello = Encoding.UTF8.GetBytes("Hello, world."); + var mh = MultiHash.ComputeHash(hello); + Assert.IsFalse(mh.IsIdentityHash); + CollectionAssert.AreNotEqual(hello, mh.Digest); + + mh = MultiHash.ComputeHash(hello, "identity"); + Assert.IsTrue(mh.IsIdentityHash); + CollectionAssert.AreEqual(hello, mh.Digest); + + var mh1 = new MultiHash(mh.ToBase58()); + Assert.AreEqual(mh, mh1); + + mh = MultiHash.ComputeHash(hello, "id"); + Assert.IsTrue(mh.IsIdentityHash); + CollectionAssert.AreEqual(hello, mh.Digest); + + mh1 = new MultiHash(mh.ToBase58()); + Assert.AreEqual(mh, mh1); + } + + [TestMethod] + public void Binary() + { + var mh = new MultiHash("QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB"); + Assert.AreEqual("sha2-256", mh.Algorithm.Name); + Assert.AreEqual(32, mh.Digest.Length); + + var binary = mh.ToArray(); + var mh1 = new MultiHash(binary); + Assert.AreEqual(mh.Algorithm.Name, mh1.Algorithm.Name); + CollectionAssert.AreEqual(mh.Digest, mh1.Digest); + } + + [TestMethod] + public void JsonSerialization() + { + var a = new MultiHash("QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB"); + var json = JsonConvert.SerializeObject(a); + Assert.AreEqual($"\"{a}\"", json); + var b = JsonConvert.DeserializeObject(json); + Assert.AreEqual(a, b); + + json = JsonConvert.SerializeObject(null); + b = JsonConvert.DeserializeObject(json); + Assert.IsNull(b); + } + + [TestMethod] + public void CodeToName() + { + Assert.AreEqual("sha2-512", MultiHash.GetHashAlgorithmName(0x13)); + ExceptionAssert.Throws( + () => MultiHash.GetHashAlgorithmName(0xbadbad)); + } + + [TestMethod] + public void GetAlgorithmByName() + { + Assert.IsNotNull(MultiHash.GetHashAlgorithm()); + Assert.IsNotNull(MultiHash.GetHashAlgorithm("sha2-512")); + var _ = ExceptionAssert.Throws(() => + { + var _ = MultiHash.GetHashAlgorithm("unknown"); + }); + } + } +} diff --git a/src/MultiFormats.Tests/NetworkProtocolTest.cs b/src/MultiFormats.Tests/NetworkProtocolTest.cs new file mode 100644 index 0000000000..38956ec043 --- /dev/null +++ b/src/MultiFormats.Tests/NetworkProtocolTest.cs @@ -0,0 +1,67 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using Google.Protobuf; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MultiFormats.Tests +{ + [TestClass] + public class NetworkProtocolTest + { + [TestMethod] + public void Stringing() { Assert.AreEqual("/tcp/8080", new MultiAddress("/tcp/8080").Protocols[0].ToString()); } + + [TestMethod] + public void Register_Name_Already_Exists() + { + ExceptionAssert.Throws(() => NetworkProtocol.Register()); + } + + [TestMethod] + public void Register_Code_Already_Exists() + { + ExceptionAssert.Throws(() => NetworkProtocol.Register()); + } + + private sealed class NameExists : NetworkProtocol + { + public override string Name => "tcp"; + public override uint Code => 0x7FFF; + public override void ReadValue(CodedInputStream stream) { } + public override void ReadValue(TextReader stream) { } + public override void WriteValue(CodedOutputStream stream) { } + } + + private sealed class CodeExists : NetworkProtocol + { + public override string Name => "x-tcp"; + public override uint Code => 6; + public override void ReadValue(CodedInputStream stream) { } + public override void ReadValue(TextReader stream) { } + public override void WriteValue(CodedOutputStream stream) { } + } + } +} diff --git a/src/MultiFormats.Tests/Registry/CodecTest.cs b/src/MultiFormats.Tests/Registry/CodecTest.cs new file mode 100644 index 0000000000..f3a3b237d1 --- /dev/null +++ b/src/MultiFormats.Tests/Registry/CodecTest.cs @@ -0,0 +1,70 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats.Registry; + +namespace MultiFormats.Tests.Registry +{ + [TestClass] + public class CodecTest + { + [TestMethod] + public void Bad_Name() + { + ExceptionAssert.Throws(() => Codec.Register(null, 1)); + ExceptionAssert.Throws(() => Codec.Register("", 1)); + ExceptionAssert.Throws(() => Codec.Register(" ", 1)); + } + + [TestMethod] + public void Name_Already_Exists() { ExceptionAssert.Throws(() => Codec.Register("raw", 1)); } + + [TestMethod] + public void Code_Already_Exists() + { + ExceptionAssert.Throws(() => Codec.Register("raw-x", 0x55)); + } + + [TestMethod] + public void Algorithms_Are_Enumerable() { Assert.AreNotEqual(0, Codec.All.Count()); } + + [TestMethod] + public void Register() + { + var codec = Codec.Register("something-new", 0x0bad); + try + { + Assert.AreEqual("something-new", codec.Name); + Assert.AreEqual("something-new", codec.ToString()); + Assert.AreEqual(0x0bad, codec.Code); + } + finally + { + Codec.Deregister(codec); + } + } + } +} diff --git a/src/MultiFormats.Tests/Registry/HashingAlgorithmTest.cs b/src/MultiFormats.Tests/Registry/HashingAlgorithmTest.cs new file mode 100644 index 0000000000..df4284f97d --- /dev/null +++ b/src/MultiFormats.Tests/Registry/HashingAlgorithmTest.cs @@ -0,0 +1,135 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats.Registry; + +namespace MultiFormats.Tests.Registry +{ + [TestClass] + public class HashingAlgorithmTest + { + [TestMethod] + public void GetHasher() + { + using (var hasher = HashingAlgorithm.GetAlgorithm("sha3-256")) + { + Assert.IsNotNull(hasher); + var input = new byte[] + { + 0xe9 + }; + var expected = "f0d04dd1e6cfc29a4460d521796852f25d9ef8d28b44ee91ff5b759d72c1e6d6".ToHexBuffer(); + + var actual = hasher.ComputeHash(input); + CollectionAssert.AreEqual(expected, actual); + } + } + + [TestMethod] + public void GetHasher_Unknown() + { + ExceptionAssert.Throws(() => HashingAlgorithm.GetAlgorithm("unknown")); + } + + [TestMethod] + public void GetMetadata() + { + var info = HashingAlgorithm.GetAlgorithmMetadata("sha3-256"); + Assert.IsNotNull(info); + Assert.AreEqual("sha3-256", info.Name); + Assert.AreEqual(0x16, info.Code); + Assert.AreEqual(256 / 8, info.DigestSize); + Assert.IsNotNull(info.Hasher); + } + + [TestMethod] + public void GetMetadata_Unknown() + { + ExceptionAssert.Throws(() => HashingAlgorithm.GetAlgorithmMetadata("unknown")); + } + + [TestMethod] + public void GetMetadata_Alias() + { + var info = HashingAlgorithm.GetAlgorithmMetadata("id"); + Assert.IsNotNull(info); + Assert.AreEqual("identity", info.Name); + Assert.AreEqual(0, info.Code); + Assert.AreEqual(0, info.DigestSize); + Assert.IsNotNull(info.Hasher); + } + + [TestMethod] + public void HashingAlgorithm_Bad_Name() + { + ExceptionAssert.Throws(() => HashingAlgorithm.Register(null, 1, 1)); + ExceptionAssert.Throws(() => HashingAlgorithm.Register("", 1, 1)); + ExceptionAssert.Throws(() => HashingAlgorithm.Register(" ", 1, 1)); + } + + [TestMethod] + public void HashingAlgorithm_Name_Already_Exists() + { + ExceptionAssert.Throws(() => HashingAlgorithm.Register("sha1", 0x11, 1)); + } + + [TestMethod] + public void HashingAlgorithm_Number_Already_Exists() + { + ExceptionAssert.Throws(() => HashingAlgorithm.Register("sha1-x", 0x11, 1)); + } + + [TestMethod] + public void HashingAlgorithms_Are_Enumerable() { Assert.IsTrue(5 <= HashingAlgorithm.All.Count()); } + + [TestMethod] + public void HashingAlgorithm_Bad_Alias() + { + ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias(null, "sha1")); + ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias("", "sha1")); + ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias(" ", "sha1")); + } + + [TestMethod] + public void HashingAlgorithm_Alias_Already_Exists() + { + ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias("id", "identity")); + } + + [TestMethod] + public void HashingAlgorithm_Alias_Target_Does_Not_Exist() + { + ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias("foo", "sha1-x")); + } + + [TestMethod] + public void HashingAlgorithm_Alias_Target_Is_Bad() + { + ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias("foo", " ")); + } + } +} diff --git a/src/MultiFormats.Tests/Registry/MultibaseAlgorithmTest.cs b/src/MultiFormats.Tests/Registry/MultibaseAlgorithmTest.cs new file mode 100644 index 0000000000..e1f2b61f35 --- /dev/null +++ b/src/MultiFormats.Tests/Registry/MultibaseAlgorithmTest.cs @@ -0,0 +1,93 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using MultiFormats.Registry; + +namespace MultiFormats.Tests.Registry +{ + [TestClass] + public class MultiBaseAlgorithmTest + { + [TestMethod] + public void Bad_Name() + { + ExceptionAssert.Throws(() => MultiBaseAlgorithm.Register(null, '?')); + ExceptionAssert.Throws(() => MultiBaseAlgorithm.Register("", '?')); + ExceptionAssert.Throws(() => MultiBaseAlgorithm.Register(" ", '?')); + } + + [TestMethod] + public void Name_Already_Exists() + { + ExceptionAssert.Throws(() => MultiBaseAlgorithm.Register("base58btc", 'z')); + } + + [TestMethod] + public void Code_Already_Exists() + { + ExceptionAssert.Throws(() => MultiBaseAlgorithm.Register("base58btc-x", 'z')); + } + + [TestMethod] + public void Algorithms_Are_Enumerable() { Assert.AreNotEqual(0, MultiBaseAlgorithm.All.Count()); } + + [TestMethod] + public void Roundtrip_All_Algorithms() + { + var bytes = new byte[] + { + 1, 2, 3, 4, 5 + }; + + foreach (var alg in MultiBaseAlgorithm.All) + { + var s = alg.Encode(bytes); + CollectionAssert.AreEqual(bytes, alg.Decode(s), alg.Name); + } + } + + [TestMethod] + public void Name_Is_Also_ToString() + { + foreach (var alg in MultiBaseAlgorithm.All) Assert.AreEqual(alg.Name, alg.ToString()); + } + + [TestMethod] + public void Known_But_NYI() + { + var alg = MultiBaseAlgorithm.Register("nyi", 'n'); + try + { + ExceptionAssert.Throws(() => alg.Encode(null)); + ExceptionAssert.Throws(() => alg.Decode(null)); + } + finally + { + MultiBaseAlgorithm.Deregister(alg); + } + } + } +} diff --git a/src/MultiFormats.Tests/VarintTest.cs b/src/MultiFormats.Tests/VarintTest.cs new file mode 100644 index 0000000000..1a31d7e692 --- /dev/null +++ b/src/MultiFormats.Tests/VarintTest.cs @@ -0,0 +1,168 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace MultiFormats.Tests +{ + [TestClass] + public class VarintTest + { + [TestMethod] + public void Zero() + { + var x = new byte[] + { + 0 + }; + Assert.AreEqual(1, Varint.RequiredBytes(0)); + CollectionAssert.AreEqual(x, Varint.Encode(0)); + Assert.AreEqual(0, Varint.DecodeInt32(x)); + } + + [TestMethod] + public void ThreeHundred() + { + var x = new byte[] + { + 0xAC, 0x02 + }; + Assert.AreEqual(2, Varint.RequiredBytes(300)); + CollectionAssert.AreEqual(x, Varint.Encode(300)); + Assert.AreEqual(300, Varint.DecodeInt32(x)); + } + + [TestMethod] + public void Decode_From_Offset() + { + var x = new byte[] + { + 0x00, 0xAC, 0x02 + }; + Assert.AreEqual(300, Varint.DecodeInt32(x, 1)); + } + + [TestMethod] + public void MaxLong() + { + var x = "ffffffffffffffff7f".ToHexBuffer(); + Assert.AreEqual(9, Varint.RequiredBytes(long.MaxValue)); + CollectionAssert.AreEqual(x, Varint.Encode(long.MaxValue)); + Assert.AreEqual(long.MaxValue, Varint.DecodeInt64(x)); + } + + [TestMethod] + public void Encode_Negative() { ExceptionAssert.Throws(() => Varint.Encode(-1)); } + + [TestMethod] + public void TooBig_Int32() + { + var bytes = Varint.Encode((long) int.MaxValue + 1); + ExceptionAssert.Throws(() => Varint.DecodeInt32(bytes)); + } + + [TestMethod] + public void TooBig_Int64() + { + var bytes = "ffffffffffffffffff7f".ToHexBuffer(); + ExceptionAssert.Throws(() => Varint.DecodeInt64(bytes)); + } + + [TestMethod] + public void Unterminated() + { + var bytes = "ff".ToHexBuffer(); + ExceptionAssert.Throws(() => Varint.DecodeInt64(bytes)); + } + + [TestMethod] + public void Empty() + { + var bytes = new byte[0]; + ExceptionAssert.Throws(() => Varint.DecodeInt64(bytes)); + } + + [TestMethod] + public async Task WriteAsync() + { + await using (var ms = new MemoryStream()) + { + await ms.WriteVarintAsync(long.MaxValue); + ms.Position = 0; + Assert.AreEqual(long.MaxValue, ms.ReadVarint64()); + } + } + + [TestMethod] + public void WriteAsync_Negative() + { + var ms = new MemoryStream(); + ExceptionAssert.Throws(() => ms.WriteVarintAsync(-1).Wait()); + } + + [TestMethod] + public void WriteAsync_Cancel() + { + var ms = new MemoryStream(); + var cs = new CancellationTokenSource(); + cs.Cancel(); + ExceptionAssert.Throws(() => ms.WriteVarintAsync(0, cs.Token).Wait()); + } + + [TestMethod] + public async Task ReadAsync() + { + await using (var ms = new MemoryStream("ffffffffffffffff7f".ToHexBuffer())) + { + var v = await ms.ReadVarint64Async(); + Assert.AreEqual(long.MaxValue, v); + } + } + + [TestMethod] + public void ReadAsync_Cancel() + { + var ms = new MemoryStream(new byte[] + { + 0 + }); + var cs = new CancellationTokenSource(); + cs.Cancel(); + ExceptionAssert.Throws(() => ms.ReadVarint32Async(cs.Token).Wait()); + } + + [TestMethod] + public void Example() + { + for (long v = 1; v <= 0xFFFFFFFL; v = v << 4) + { + Console.Write($"| {v} (0x{v.ToString("x")}) "); + Console.WriteLine($"| {Varint.Encode(v).ToHexString()} |"); + } + } + } +} diff --git a/src/MultiFormats/Base32.cs b/src/MultiFormats/Base32.cs new file mode 100644 index 0000000000..c22ed46dc3 --- /dev/null +++ b/src/MultiFormats/Base32.cs @@ -0,0 +1,100 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace MultiFormats +{ + /// + /// A codec for Base-32. + /// + /// + /// + /// A codec for Base-32, and . Adds the extension method + /// to encode a byte array and to decode a Base-32 string. + /// + /// + /// and produce the lower case form of + /// with no padding. + /// and are case-insensitive and + /// allow optional padding. + /// + /// + /// A thin wrapper around . + /// + /// + public static class Base32 + { + /// + /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is + /// encoded with base-32 characters. + /// s + /// + /// An array of 8-bit unsigned integers. + /// + /// + /// The string representation, in base 32, of the contents of . + /// + public static string Encode(byte[] input) + { + return SimpleBase.Base32.Rfc4648.Encode(input, false).ToLowerInvariant(); + } + + /// + /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is + /// encoded with base-32 digits. + /// + /// + /// An array of 8-bit unsigned integers. + /// + /// + /// The string representation, in base 32, of the contents of . + /// + public static string ToBase32(this byte[] bytes) { return Encode(bytes); } + + /// + /// Converts the specified , which encodes binary data as base 32 digits, + /// to an equivalent 8-bit unsigned integer array. + /// + /// + /// The base 32 string to convert. + /// + /// + /// An array of 8-bit unsigned integers that is equivalent to . + /// + /// + /// is case-insensitive and allows padding. + /// + public static byte[] Decode(string input) { return SimpleBase.Base32.Rfc4648.Decode(input).ToArray(); } + + /// + /// Converts the specified , which encodes binary data as base 32 digits, + /// to an equivalent 8-bit unsigned integer array. + /// + /// + /// The base 32 string to convert; case-insensitive and allows padding. + /// + /// + /// An array of 8-bit unsigned integers that is equivalent to . + /// + public static byte[] FromBase32(this string s) { return Decode(s); } + } +} diff --git a/src/MultiFormats/Base32z.cs b/src/MultiFormats/Base32z.cs new file mode 100644 index 0000000000..35fa5c94c1 --- /dev/null +++ b/src/MultiFormats/Base32z.cs @@ -0,0 +1,45 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using SimpleBase; + +namespace MultiFormats +{ + /// + /// Base32 encoding designed to be easier for human use and more compact. + /// + /// + /// Commonly referred to as 'z-base-32'. + /// + /// + public static class Base32Z + { + private static readonly Base32Alphabet Alphabet = + new Base32Alphabet("ybndrfg8ejkmcpqxot1uwisza345h769"); + + /// + /// The encoder/decoder for z-base-32. + /// + public static readonly SimpleBase.Base32 Codec = new(Alphabet); + } +} diff --git a/src/MultiFormats/Base58.cs b/src/MultiFormats/Base58.cs new file mode 100644 index 0000000000..2135903f15 --- /dev/null +++ b/src/MultiFormats/Base58.cs @@ -0,0 +1,91 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace MultiFormats +{ + /// + /// A codec for IPFS Base-58. + /// + /// + /// + /// A codec for Base-58, and . Adds the extension method + /// to encode a byte array and to decode a Base-58 string. + /// + /// + /// This is just thin wrapper of . + /// + /// + /// This codec uses the BitCoin alphabet not Flickr's. + /// + /// + public static class Base58 + { + /// + /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is + /// encoded with base-58 characters. + /// s + /// + /// An array of 8-bit unsigned integers. + /// + /// + /// The string representation, in base 58, of the contents of . + /// + public static string Encode(byte[] bytes) { return SimpleBase.Base58.Bitcoin.Encode(bytes); } + + /// + /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is + /// encoded with base-58 digits. + /// + /// + /// An array of 8-bit unsigned integers. + /// + /// + /// The string representation, in base 58, of the contents of . + /// + public static string ToBase58(this byte[] bytes) { return Encode(bytes); } + + /// + /// Converts the specified , which encodes binary data as base 58 digits, + /// to an equivalent 8-bit unsigned integer array. + /// + /// + /// The base 58 string to convert. + /// + /// + /// An array of 8-bit unsigned integers that is equivalent to . + /// + public static byte[] Decode(string s) { return SimpleBase.Base58.Bitcoin.Decode(s).ToArray(); } + + /// + /// Converts the specified , which encodes binary data as base 58 digits, + /// to an equivalent 8-bit unsigned integer array. + /// + /// + /// The base 58 string to convert. + /// + /// + /// An array of 8-bit unsigned integers that is equivalent to . + /// + public static byte[] FromBase58(this string s) { return Decode(s); } + } +} diff --git a/src/MultiFormats/Base64NoPad.cs b/src/MultiFormats/Base64NoPad.cs new file mode 100644 index 0000000000..0f8c7a125a --- /dev/null +++ b/src/MultiFormats/Base64NoPad.cs @@ -0,0 +1,102 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; + +namespace MultiFormats +{ + /// + /// A codec for Base-64 (RFC 4648) with no padding. + /// + /// + /// + /// A codec for Base-64, and . Adds the extension method + /// to encode a byte array and to decode a Base-64 string. + /// + /// + public static class Base64NoPad + { + /// + /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is + /// encoded with base-64 characters. + /// s + /// + /// An array of 8-bit unsigned integers. + /// + /// + /// The string representation, in base 64, of the contents of . + /// + public static string Encode(byte[] bytes) { return Convert.ToBase64String(bytes).TrimEnd('='); } + + /// + /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is + /// encoded with base-64 digits. + /// + /// + /// An array of 8-bit unsigned integers. + /// s + /// + /// The string representation, in base 64, of the contents of . + /// + public static string ToBase64NoPad(this byte[] bytes) { return Encode(bytes); } + + /// + /// Converts the specified , which encodes binary data as base 64 digits, + /// to an equivalent 8-bit unsigned integer array. + /// + /// + /// The base 64 string to convert. + /// + /// + /// An array of 8-bit unsigned integers that is equivalent to . + /// + public static byte[] Decode(string s) + { + switch (s.Length % 4) // Pad with trailing '='s + { + case 0: break; // No pad chars in this case + case 2: + s += "=="; + break; // Two pad chars + case 3: + s += "="; + break; // One pad char + default: throw new Exception("Illegal base64 string!"); + } + + return Convert.FromBase64String(s); // Standard base64 decoder + } + + /// + /// Converts the specified , which encodes binary data as base 64 digits, + /// to an equivalent 8-bit unsigned integer array. + /// + /// + /// The base 64 string to convert. + /// + /// + /// An array of 8-bit unsigned integers that is equivalent to . + /// + public static byte[] FromBase64NoPad(this string s) { return Decode(s); } + } +} diff --git a/src/MultiFormats/Base64Url.cs b/src/MultiFormats/Base64Url.cs new file mode 100644 index 0000000000..98f3232e7e --- /dev/null +++ b/src/MultiFormats/Base64Url.cs @@ -0,0 +1,117 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; + +namespace MultiFormats +{ + /// + /// A codec for Base-64 URL (RFC 4648). + /// + /// + /// + /// A codec for Base-64 URL, and . Adds the extension method + /// to encode a byte array and to decode a Base-64 URL string. + /// + /// + /// The original code was found at . + /// + /// + public static class Base64Url + { + /// + /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is + /// encoded with base-64 URL characters. + /// s + /// + /// An array of 8-bit unsigned integers. + /// + /// + /// The string representation, in base 64, of the contents of . + /// + public static string Encode(byte[] bytes) + { + var s = Convert.ToBase64String(bytes); // Standard base64 encoder + + s = s.TrimEnd('='); // Remove any trailing '='s + s = s.Replace('+', '-'); // 62nd char of encoding + s = s.Replace('/', '_'); // 63rd char of encoding + + return s; + } + + /// + /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is + /// encoded with base-64 URL digits. + /// + /// + /// An array of 8-bit unsigned integers. + /// s + /// + /// The string representation, in base 64, of the contents of . + /// + public static string ToBase64Url(this byte[] bytes) { return Encode(bytes); } + + /// + /// Converts the specified , which encodes binary data as base 64 URL digits, + /// to an equivalent 8-bit unsigned integer array. + /// + /// + /// The base 64 string to convert. + /// + /// + /// An array of 8-bit unsigned integers that is equivalent to . + /// + public static byte[] Decode(string s) + { + s = s.Replace('-', '+'); // 62nd char of encoding + s = s.Replace('_', '/'); // 63rd char of encoding + + switch (s.Length % 4) // Pad with trailing '='s + { + case 0: break; // No pad chars in this case + case 2: + s += "=="; + break; // Two pad chars + case 3: + s += "="; + break; // One pad char + default: throw new Exception("Illegal base64url string!"); + } + + return Convert.FromBase64String(s); // Standard base64 decoder + } + + /// + /// Converts the specified , which encodes binary data as base 64 url digits, + /// to an equivalent 8-bit unsigned integer array. + /// + /// + /// The base 64 string to convert. + /// + /// + /// An array of 8-bit unsigned integers that is equivalent to . + /// + public static byte[] FromBase64Url(this string s) { return Decode(s); } + } +} diff --git a/src/MultiFormats/Cryptography/BouncyDigest.cs b/src/MultiFormats/Cryptography/BouncyDigest.cs new file mode 100644 index 0000000000..f56246791e --- /dev/null +++ b/src/MultiFormats/Cryptography/BouncyDigest.cs @@ -0,0 +1,58 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +namespace MultiFormats.Cryptography +{ + /// + /// Thin wrapper around bouncy castle digests. + /// + /// + /// Makes a Bouncy Caslte IDigest speak .Net HashAlgorithm. + /// + internal class BouncyDigest : System.Security.Cryptography.HashAlgorithm + { + private Org.BouncyCastle.Crypto.IDigest digest; + + /// + /// Wrap the bouncy castle digest. + /// + public BouncyDigest(Org.BouncyCastle.Crypto.IDigest digest) { this.digest = digest; } + + /// + public override void Initialize() { digest.Reset(); } + + /// + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + digest.BlockUpdate(array, ibStart, cbSize); + } + + /// + protected override byte[] HashFinal() + { + var output = new byte[digest.GetDigestSize()]; + digest.DoFinal(output, 0); + return output; + } + } +} diff --git a/src/MultiFormats/Cryptography/DoubleSha256.cs b/src/MultiFormats/Cryptography/DoubleSha256.cs new file mode 100644 index 0000000000..7b8c360f72 --- /dev/null +++ b/src/MultiFormats/Cryptography/DoubleSha256.cs @@ -0,0 +1,56 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Security.Cryptography; + +namespace MultiFormats.Cryptography +{ + internal class DoubleSha256 : HashAlgorithm + { + private HashAlgorithm _digest = SHA256.Create(); + private byte[] _round1; + + public override void Initialize() + { + _digest.Initialize(); + _round1 = null; + } + + public override int HashSize => _digest.HashSize; + + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + if (_round1 != null) + throw new NotSupportedException("Already called."); + + _round1 = _digest.ComputeHash(array, ibStart, cbSize); + } + + protected override byte[] HashFinal() + { + _digest.Initialize(); + return _digest.ComputeHash(_round1); + } + } +} diff --git a/src/MultiFormats/Cryptography/IdentityHash.cs b/src/MultiFormats/Cryptography/IdentityHash.cs new file mode 100644 index 0000000000..c5a474a032 --- /dev/null +++ b/src/MultiFormats/Cryptography/IdentityHash.cs @@ -0,0 +1,52 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Security.Cryptography; + +namespace MultiFormats.Cryptography +{ + internal class IdentityHash : HashAlgorithm + { + private byte[] _digest; + + public override void Initialize() { } + + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + if (_digest == null) + { + _digest = new byte[cbSize]; + Buffer.BlockCopy(array, ibStart, _digest, 0, cbSize); + return; + } + + var buffer = new byte[_digest.Length + cbSize]; + Buffer.BlockCopy(_digest, 0, buffer, _digest.Length, _digest.Length); + Buffer.BlockCopy(array, ibStart, _digest, _digest.Length, cbSize); + _digest = buffer; + } + + protected override byte[] HashFinal() { return _digest; } + } +} diff --git a/src/MultiFormats/Cryptography/Keccak.cs b/src/MultiFormats/Cryptography/Keccak.cs new file mode 100644 index 0000000000..2961dcd2a0 --- /dev/null +++ b/src/MultiFormats/Cryptography/Keccak.cs @@ -0,0 +1,166 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +/* + * The package was named SHA3 it is really Keccak + * https://medium.com/@ConsenSys/are-you-really-using-sha-3-or-old-code-c5df31ad2b0 + * See + * + * The SHA3 package doesn't create .Net Standard package. + * This is a copy of https://bitbucket.org/jdluzen/sha3/raw/d1fd55dc225d18a7fb61515b62d3c8f164d2e788/SHA3/SHA3.cs + */ + +using System; + +namespace MultiFormats.Cryptography +{ + internal abstract class Keccak : System.Security.Cryptography.HashAlgorithm + { + #region Implementation + + public const int KeccakB = 1600; + public const int KeccakNumberOfRounds = 24; + public const int KeccakLaneSizeInBits = 8 * 8; + + public readonly ulong[] RoundConstants; + + protected ulong[] state; + protected byte[] Buffer; + protected int BuffLength; + + protected int keccakR; + + public int KeccakR { get => keccakR; protected set => keccakR = value; } + + public int SizeInBytes => KeccakR / 8; + + public int HashByteLength => HashSizeValue / 8; + + public override bool CanReuseTransform => true; + + protected Keccak(int hashBitLength) + { + if (hashBitLength != 224 && hashBitLength != 256 && hashBitLength != 384 && hashBitLength != 512) + { + throw new ArgumentException("hashBitLength must be 224, 256, 384, or 512", nameof(hashBitLength)); + } + + Initialize(); + HashSizeValue = hashBitLength; + switch (hashBitLength) + { + case 224: + KeccakR = 1152; + break; + case 256: + KeccakR = 1088; + break; + case 384: + KeccakR = 832; + break; + case 512: + KeccakR = 576; + break; + } + + RoundConstants = new[] + { + 0x0000000000000001UL, + 0x0000000000008082UL, + 0x800000000000808aUL, + 0x8000000080008000UL, + 0x000000000000808bUL, + 0x0000000080000001UL, + 0x8000000080008081UL, + 0x8000000000008009UL, + 0x000000000000008aUL, + 0x0000000000000088UL, + 0x0000000080008009UL, + 0x000000008000000aUL, + 0x000000008000808bUL, + 0x800000000000008bUL, + 0x8000000000008089UL, + 0x8000000000008003UL, + 0x8000000000008002UL, + 0x8000000000000080UL, + 0x000000000000800aUL, + 0x800000008000000aUL, + 0x8000000080008081UL, + 0x8000000000008080UL, + 0x0000000080000001UL, + 0x8000000080008008UL + }; + } + + protected ulong Rol(ulong a, int offset) + { + return (a << (offset % KeccakLaneSizeInBits)) ^ + (a >> (KeccakLaneSizeInBits - offset % KeccakLaneSizeInBits)); + } + + protected void AddToBuffer(byte[] array, ref int offset, ref int count) + { + var amount = Math.Min(count, Buffer.Length - BuffLength); + System.Buffer.BlockCopy(array, offset, Buffer, BuffLength, amount); + offset += amount; + BuffLength += amount; + count -= amount; + } + + public override byte[] Hash => HashValue; + + public override int HashSize => HashSizeValue; + + #endregion + + public sealed override void Initialize() + { + BuffLength = 0; + state = new ulong[5 * 5]; //1600 bits + HashValue = null; + } + + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + if (array == null) + { + throw new ArgumentNullException(nameof(array)); + } + + if (ibStart < 0) + { + throw new ArgumentOutOfRangeException(nameof(ibStart)); + } + + if (cbSize > array.Length) + { + throw new ArgumentOutOfRangeException(nameof(cbSize)); + } + + if (ibStart + cbSize > array.Length) + { + throw new ArgumentOutOfRangeException("" + "ibStart or cbSize"); + } + } + } +} diff --git a/src/MultiFormats/Cryptography/KeccakManaged.cs b/src/MultiFormats/Cryptography/KeccakManaged.cs new file mode 100644 index 0000000000..74969ad77b --- /dev/null +++ b/src/MultiFormats/Cryptography/KeccakManaged.cs @@ -0,0 +1,374 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +// The SHA3 doesn't create .Net Standard package. +// This is a copy of https://bitbucket.org/jdluzen/sha3/raw/d1fd55dc225d18a7fb61515b62d3c8f164d2e788/SHA3Managed/SHA3Managed.cs + +using System; + +namespace MultiFormats.Cryptography +{ + internal sealed class KeccakManaged : Keccak + { + public KeccakManaged(int hashBitLength) + : base(hashBitLength) { } + + protected override void HashCore(byte[] array, int ibStart, int cbSize) + { + base.HashCore(array, ibStart, cbSize); + if (cbSize == 0) + { + return; + } + + var sizeInBytes = SizeInBytes; + if (Buffer == null) + { + Buffer = new byte[sizeInBytes]; + } + + var stride = sizeInBytes >> 3; + var utemps = new ulong[stride]; + if (BuffLength == sizeInBytes) + { + throw new Exception("Unexpected error, the internal buffer is full"); + } + + AddToBuffer(array, ref ibStart, ref cbSize); + if (BuffLength == sizeInBytes) //buffer full + { + System.Buffer.BlockCopy(Buffer, 0, utemps, 0, sizeInBytes); + KeccakF(utemps, stride); + BuffLength = 0; + } + + for (; cbSize >= sizeInBytes; cbSize -= sizeInBytes, ibStart += sizeInBytes) + { + System.Buffer.BlockCopy(array, ibStart, utemps, 0, sizeInBytes); + KeccakF(utemps, stride); + } + + if (cbSize <= 0) + { + return; + } + + System.Buffer.BlockCopy(array, ibStart, Buffer, BuffLength, cbSize); + BuffLength += cbSize; + } + + protected override byte[] HashFinal() + { + var sizeInBytes = SizeInBytes; + var outb = new byte[HashByteLength]; + + // padding + if (Buffer == null) + { + Buffer = new byte[sizeInBytes]; + } + else + { + Array.Clear(Buffer, BuffLength, sizeInBytes - BuffLength); + } + + Buffer[BuffLength++] = 1; + Buffer[sizeInBytes - 1] |= 0x80; + var stride = sizeInBytes >> 3; + var utemps = new ulong[stride]; + System.Buffer.BlockCopy(Buffer, 0, utemps, 0, sizeInBytes); + KeccakF(utemps, stride); + System.Buffer.BlockCopy(state, 0, outb, 0, HashByteLength); + return outb; + } + + private void KeccakF(ulong[] inb, int laneCount) + { + while (--laneCount >= 0) + { + state[laneCount] ^= inb[laneCount]; + } + + ulong Aba, Abe, Abi, Abo, Abu; + ulong Aga, Age, Agi, Ago, Agu; + ulong Aka, Ake, Aki, Ako, Aku; + ulong Ama, Ame, Ami, Amo, Amu; + ulong Asa, Ase, Asi, Aso, Asu; + ulong BCa, BCe, BCi, BCo, BCu; + ulong Da, De, Di, Do, Du; + ulong Eba, Ebe, Ebi, Ebo, Ebu; + ulong Ega, Ege, Egi, Ego, Egu; + ulong Eka, Eke, Eki, Eko, Eku; + ulong Ema, Eme, Emi, Emo, Emu; + ulong Esa, Ese, Esi, Eso, Esu; + var round = laneCount; + + //copyFromState(A, state) + Aba = state[0]; + Abe = state[1]; + Abi = state[2]; + Abo = state[3]; + Abu = state[4]; + Aga = state[5]; + Age = state[6]; + Agi = state[7]; + Ago = state[8]; + Agu = state[9]; + Aka = state[10]; + Ake = state[11]; + Aki = state[12]; + Ako = state[13]; + Aku = state[14]; + Ama = state[15]; + Ame = state[16]; + Ami = state[17]; + Amo = state[18]; + Amu = state[19]; + Asa = state[20]; + Ase = state[21]; + Asi = state[22]; + Aso = state[23]; + Asu = state[24]; + + for (round = 0; round < KeccakNumberOfRounds; round += 2) + { + // prepareTheta + BCa = Aba ^ Aga ^ Aka ^ Ama ^ Asa; + BCe = Abe ^ Age ^ Ake ^ Ame ^ Ase; + BCi = Abi ^ Agi ^ Aki ^ Ami ^ Asi; + BCo = Abo ^ Ago ^ Ako ^ Amo ^ Aso; + BCu = Abu ^ Agu ^ Aku ^ Amu ^ Asu; + + //thetaRhoPiChiIotaPrepareTheta(round , A, E) + Da = BCu ^ Rol(BCe, 1); + De = BCa ^ Rol(BCi, 1); + Di = BCe ^ Rol(BCo, 1); + Do = BCi ^ Rol(BCu, 1); + Du = BCo ^ Rol(BCa, 1); + + Aba ^= Da; + BCa = Aba; + Age ^= De; + BCe = Rol(Age, 44); + Aki ^= Di; + BCi = Rol(Aki, 43); + Amo ^= Do; + BCo = Rol(Amo, 21); + Asu ^= Du; + BCu = Rol(Asu, 14); + Eba = BCa ^ (~BCe & BCi); + Eba ^= RoundConstants[round]; + Ebe = BCe ^ (~BCi & BCo); + Ebi = BCi ^ (~BCo & BCu); + Ebo = BCo ^ (~BCu & BCa); + Ebu = BCu ^ (~BCa & BCe); + + Abo ^= Do; + BCa = Rol(Abo, 28); + Agu ^= Du; + BCe = Rol(Agu, 20); + Aka ^= Da; + BCi = Rol(Aka, 3); + Ame ^= De; + BCo = Rol(Ame, 45); + Asi ^= Di; + BCu = Rol(Asi, 61); + Ega = BCa ^ (~BCe & BCi); + Ege = BCe ^ (~BCi & BCo); + Egi = BCi ^ (~BCo & BCu); + Ego = BCo ^ (~BCu & BCa); + Egu = BCu ^ (~BCa & BCe); + + Abe ^= De; + BCa = Rol(Abe, 1); + Agi ^= Di; + BCe = Rol(Agi, 6); + Ako ^= Do; + BCi = Rol(Ako, 25); + Amu ^= Du; + BCo = Rol(Amu, 8); + Asa ^= Da; + BCu = Rol(Asa, 18); + Eka = BCa ^ (~BCe & BCi); + Eke = BCe ^ (~BCi & BCo); + Eki = BCi ^ (~BCo & BCu); + Eko = BCo ^ (~BCu & BCa); + Eku = BCu ^ (~BCa & BCe); + + Abu ^= Du; + BCa = Rol(Abu, 27); + Aga ^= Da; + BCe = Rol(Aga, 36); + Ake ^= De; + BCi = Rol(Ake, 10); + Ami ^= Di; + BCo = Rol(Ami, 15); + Aso ^= Do; + BCu = Rol(Aso, 56); + Ema = BCa ^ (~BCe & BCi); + Eme = BCe ^ (~BCi & BCo); + Emi = BCi ^ (~BCo & BCu); + Emo = BCo ^ (~BCu & BCa); + Emu = BCu ^ (~BCa & BCe); + + Abi ^= Di; + BCa = Rol(Abi, 62); + Ago ^= Do; + BCe = Rol(Ago, 55); + Aku ^= Du; + BCi = Rol(Aku, 39); + Ama ^= Da; + BCo = Rol(Ama, 41); + Ase ^= De; + BCu = Rol(Ase, 2); + Esa = BCa ^ (~BCe & BCi); + Ese = BCe ^ (~BCi & BCo); + Esi = BCi ^ (~BCo & BCu); + Eso = BCo ^ (~BCu & BCa); + Esu = BCu ^ (~BCa & BCe); + + // prepareTheta + BCa = Eba ^ Ega ^ Eka ^ Ema ^ Esa; + BCe = Ebe ^ Ege ^ Eke ^ Eme ^ Ese; + BCi = Ebi ^ Egi ^ Eki ^ Emi ^ Esi; + BCo = Ebo ^ Ego ^ Eko ^ Emo ^ Eso; + BCu = Ebu ^ Egu ^ Eku ^ Emu ^ Esu; + + //thetaRhoPiChiIotaPrepareTheta(round+1, E, A) + Da = BCu ^ Rol(BCe, 1); + De = BCa ^ Rol(BCi, 1); + Di = BCe ^ Rol(BCo, 1); + Do = BCi ^ Rol(BCu, 1); + Du = BCo ^ Rol(BCa, 1); + + Eba ^= Da; + BCa = Eba; + Ege ^= De; + BCe = Rol(Ege, 44); + Eki ^= Di; + BCi = Rol(Eki, 43); + Emo ^= Do; + BCo = Rol(Emo, 21); + Esu ^= Du; + BCu = Rol(Esu, 14); + Aba = BCa ^ (~BCe & BCi); + Aba ^= RoundConstants[round + 1]; + Abe = BCe ^ (~BCi & BCo); + Abi = BCi ^ (~BCo & BCu); + Abo = BCo ^ (~BCu & BCa); + Abu = BCu ^ (~BCa & BCe); + + Ebo ^= Do; + BCa = Rol(Ebo, 28); + Egu ^= Du; + BCe = Rol(Egu, 20); + Eka ^= Da; + BCi = Rol(Eka, 3); + Eme ^= De; + BCo = Rol(Eme, 45); + Esi ^= Di; + BCu = Rol(Esi, 61); + Aga = BCa ^ (~BCe & BCi); + Age = BCe ^ (~BCi & BCo); + Agi = BCi ^ (~BCo & BCu); + Ago = BCo ^ (~BCu & BCa); + Agu = BCu ^ (~BCa & BCe); + + Ebe ^= De; + BCa = Rol(Ebe, 1); + Egi ^= Di; + BCe = Rol(Egi, 6); + Eko ^= Do; + BCi = Rol(Eko, 25); + Emu ^= Du; + BCo = Rol(Emu, 8); + Esa ^= Da; + BCu = Rol(Esa, 18); + Aka = BCa ^ (~BCe & BCi); + Ake = BCe ^ (~BCi & BCo); + Aki = BCi ^ (~BCo & BCu); + Ako = BCo ^ (~BCu & BCa); + Aku = BCu ^ (~BCa & BCe); + + Ebu ^= Du; + BCa = Rol(Ebu, 27); + Ega ^= Da; + BCe = Rol(Ega, 36); + Eke ^= De; + BCi = Rol(Eke, 10); + Emi ^= Di; + BCo = Rol(Emi, 15); + Eso ^= Do; + BCu = Rol(Eso, 56); + Ama = BCa ^ (~BCe & BCi); + Ame = BCe ^ (~BCi & BCo); + Ami = BCi ^ (~BCo & BCu); + Amo = BCo ^ (~BCu & BCa); + Amu = BCu ^ (~BCa & BCe); + + Ebi ^= Di; + BCa = Rol(Ebi, 62); + Ego ^= Do; + BCe = Rol(Ego, 55); + Eku ^= Du; + BCi = Rol(Eku, 39); + Ema ^= Da; + BCo = Rol(Ema, 41); + Ese ^= De; + BCu = Rol(Ese, 2); + Asa = BCa ^ (~BCe & BCi); + Ase = BCe ^ (~BCi & BCo); + Asi = BCi ^ (~BCo & BCu); + Aso = BCo ^ (~BCu & BCa); + Asu = BCu ^ (~BCa & BCe); + } + + //copyToState(state, A) + state[0] = Aba; + state[1] = Abe; + state[2] = Abi; + state[3] = Abo; + state[4] = Abu; + state[5] = Aga; + state[6] = Age; + state[7] = Agi; + state[8] = Ago; + state[9] = Agu; + state[10] = Aka; + state[11] = Ake; + state[12] = Aki; + state[13] = Ako; + state[14] = Aku; + state[15] = Ama; + state[16] = Ame; + state[17] = Ami; + state[18] = Amo; + state[19] = Amu; + state[20] = Asa; + state[21] = Ase; + state[22] = Asi; + state[23] = Aso; + state[24] = Asu; + } + } +} diff --git a/src/MultiFormats/HexString.cs b/src/MultiFormats/HexString.cs new file mode 100644 index 0000000000..bacf244980 --- /dev/null +++ b/src/MultiFormats/HexString.cs @@ -0,0 +1,161 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace MultiFormats +{ + /// + /// A codec for Hexadecimal. + /// + /// + /// + /// A codec for a hexadecimal string, and . Adds the extension method + /// to encode a byte array and to decode a hexadecimal . + /// + /// + public static class HexString + { + private static readonly string[] LowerCaseHexStrings = + Enumerable.Range(byte.MinValue, byte.MaxValue + 1) + .Select(v => v.ToString("x2")) + .ToArray(); + + private static readonly string[] UpperCaseHexStrings = + Enumerable.Range(byte.MinValue, byte.MaxValue + 1) + .Select(v => v.ToString("X2")) + .ToArray(); + + private static readonly Dictionary HexBytes = + Enumerable.Range(byte.MinValue, byte.MaxValue + 1) + .SelectMany(v => new[] + { + new + { + Value = v, String = v.ToString("x2") + }, + new + { + Value = v, String = v.ToString("X2") + } + }) + .Distinct() + .ToDictionary(v => v.String, v => (byte) v.Value); + + /// + /// Converts an array of 8-bit unsigned integers to its equivalent hexadecimal string representation. + /// + /// + /// An array of 8-bit unsigned integers. + /// + /// + /// One of the format specifiers ("G" and "x" for lower-case hex digits, or "X" for the upper-case). + /// The default is "G". + /// + /// + /// The string representation, in hexadecimal, of the contents of . + /// + public static string Encode(byte[] buffer, string format = "G") + { + string[] hexStrings; + switch (format) + { + case "G": + case "x": + hexStrings = LowerCaseHexStrings; + break; + case "X": + hexStrings = UpperCaseHexStrings; + break; + default: + throw new FormatException( + string.Format("Invalid HexString format '{0}', only 'G', 'x' or 'X' are allowed.", format)); + } + + var s = new StringBuilder(buffer.Length * 2); + foreach (var v in buffer) + s.Append(hexStrings[v]); + return s.ToString(); + } + + /// + /// Converts an array of 8-bit unsigned integers to its equivalent hexadecimal string representation. + /// + /// + /// An array of 8-bit unsigned integers. + /// + /// + /// One of the format specifiers ("G" and "x" for lower-case hex digits, or "X" for the upper-case). + /// The default is "G". + /// + /// + /// The string representation, in hexadecimal, of the contents of . + /// + public static string ToHexString(this byte[] buffer, string format = "G") { return Encode(buffer, format); } + + /// + /// Converts the specified , which encodes binary data as hexadecimal digits, + /// to an equivalent 8-bit unsigned integer array. + /// + /// + /// The hexadecimal string to convert. + /// + /// + /// An array of 8-bit unsigned integers that is equivalent to . + /// + public static byte[] Decode(string s) + { + var n = s.Length; + if (n % 2 != 0) + throw new InvalidDataException("The hex string length must be a multiple of 2."); + + var buffer = new byte[n / 2]; + for (int i = 0, j = 0; i < n; i += 2, j++) + { + var hex = s.Substring(i, 2); + byte value; + if (!HexBytes.TryGetValue(hex, out value)) + throw new InvalidDataException(string.Format("'{0}' is not a valid hexadecimal byte.", hex)); + buffer[j] = value; + } + + return buffer; + } + + /// + /// Converts the specified , which encodes binary data as a hexadecimal string, + /// to an equivalent 8-bit unsigned integer array. + /// + /// + /// The hexadecimal string to convert. + /// + /// + /// An array of 8-bit unsigned integers that is equivalent to . + /// + public static byte[] ToHexBuffer(this string s) { return Decode(s); } + } +} diff --git a/src/MultiFormats/Json.cs b/src/MultiFormats/Json.cs new file mode 100644 index 0000000000..884b6848de --- /dev/null +++ b/src/MultiFormats/Json.cs @@ -0,0 +1,59 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using Newtonsoft.Json; + +namespace MultiFormats +{ + public partial class MultiHash + { + /// + /// Conversion of a to and from JSON. + /// + /// + /// The JSON is just a single string value. + /// + private sealed class Json : JsonConverter + { + public override bool CanConvert(Type objectType) { return true; } + + public override bool CanRead => true; + public override bool CanWrite => true; + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var mh = value as MultiHash; + writer.WriteValue(mh?.ToString()); + } + + public override object ReadJson(JsonReader reader, + Type objectType, + object existingValue, + JsonSerializer serializer) + { + return !(reader.Value is string s) ? null : new MultiHash(s); + } + } + } +} diff --git a/src/MultiFormats/MultiAddress.cs b/src/MultiFormats/MultiAddress.cs new file mode 100644 index 0000000000..7ba6f1518b --- /dev/null +++ b/src/MultiFormats/MultiAddress.cs @@ -0,0 +1,565 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Net; +using System.Net.Sockets; +using System.Text; +using Google.Protobuf; +using Newtonsoft.Json; + +namespace MultiFormats +{ + /// + /// A set of steps describing how to build up a connection. + /// + /// + /// A multi address emphasizes explicitness, self-description, and + /// portability. It allows applications to treat addresses as opaque tokens + /// which avoids making assumptions about the address representation (e.g. length). + /// + /// A multi address is represented as a series of protocol codes and values pairs. For example, + /// an IPFS file at a sepcific address over ipv4 and tcp is + /// "/ip4/10.1.10.10/tcp/29087/ipfs/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC". + /// + /// + /// A multi address is considered immutablle and value type equality is implemented. + /// + /// + /// + [JsonConverter(typeof(Json))] + public sealed class MultiAddress : IEquatable + { + /// + /// Creates a new instance of the class. + /// + private MultiAddress() { Protocols = new List(); } + + /// + /// The components of the MultiAddress. + /// + public List Protocols { get; } + + /// + /// Creates a new instance of the class with the string. + /// + /// + /// The string representation of a multi address, such as "/ip4/1270.0.01/tcp/5001". + /// + public MultiAddress(string s) : this() + { + if (string.IsNullOrWhiteSpace(s)) + { + return; + } + + Read(new StringReader(s)); + } + + /// + /// Creates a new instance of the class from the + /// specified . + /// + /// + /// A containing the binary representation of a + /// MultiAddress. + /// + /// + /// Reads the binary representation of from the . + /// + /// The binary representation is a sequence of network protocols. + /// + /// + public MultiAddress(Stream stream) + : this() + { + Read(stream); + } + + /// + /// Creates a new instance of the class from the + /// specified . + /// + public MultiAddress(IPAddress ip) + : this() + { + var type = ip.AddressFamily == AddressFamily.InterNetwork + ? "ip4" + : "ip6"; + Read(new StringReader($"/{type}/{ip}")); + } + + /// + /// Creates a new instance of the class from the + /// specified . + /// + public MultiAddress(IPEndPoint endpoint) + : this() + { + var type = endpoint.AddressFamily == AddressFamily.InterNetwork + ? "ip4" + : "ip6"; + Read(new StringReader($"/{type}/{endpoint.Address}/tcp/{endpoint.Port}")); + } + + /// + /// Creates a deep copy of the multi address. + /// + /// + /// A new deep copy. + /// + public MultiAddress Clone() { return new MultiAddress(ToString()); } + + /// + /// Creates a new instance of the class from the + /// specified byte array. + /// + /// ( + /// A byte array containing the binary representation of a + /// MultiAddress. + /// + /// + /// Reads the binary representation of from the . + /// + /// The binary representation is a sequence of network protocols. + /// + /// + public MultiAddress(byte[] buffer) + : this() + { + if (buffer == null || buffer.Length == 0) + { + return; + } + + Read(new MemoryStream(buffer, false)); + } + + /// + /// Gets the peer ID of the multiaddress. + /// + /// + /// The + /// Lib.P2P.Peer.Id + /// + /// as a . + /// + /// + /// When the last protocol + /// is not "ipfs" nor "p2p". + /// + /// + /// The peer ID is contained in the last protocol that + /// is "ipfs" or "p2p". For example, /ip4/10.1.10.10/tcp/29087/ipfs/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC. + /// + public MultiHash PeerId + { + get + { + var protocol = Protocols + .LastOrDefault(p => p.Name == "ipfs" || p.Name == "p2p"); + if (protocol == null) + { + throw new Exception($"'{this}' is missing the peer ID. Add the 'ipfs' or 'p2p' protocol."); + } + + return protocol.Value; + } + } + + /// + /// Determines if the peer ID is present. + /// + /// + /// true if the peer ID present; otherwise, false. + /// + /// + /// The peer ID is contained in the last protocol that + /// is "ipfs" or "p2p". For example, /ip4/10.1.10.10/tcp/29087/ipfs/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC. + /// + public bool HasPeerId { get { return Protocols.Any(p => p.Name == "ipfs" || p.Name == "p2p"); } } + + /// + /// Gets a multiaddress that ends with the peer ID. + /// + /// + /// The peer ID to end the multiaddress with. + /// + /// + /// Either the this multiadddress when it contains the + /// or a new + /// ending the . + /// + /// + /// When the mulltiaddress has the wrong peer ID. + /// + public MultiAddress WithPeerId(MultiHash peerId) + { + if (!HasPeerId) + { + return new MultiAddress(ToString() + $"/p2p/{peerId}"); + } + + var id = PeerId; + if (id != peerId) + { + throw new Exception($"Expected a multiaddress with peer ID of '{peerId}', not '{id}'."); + } + + return this; + } + + /// + /// Gets a multiaddress without the peer ID. + /// + /// + /// Either the this multiaddress when it does not contain + /// a peer ID; or a new without the peer ID. + /// + public MultiAddress WithoutPeerId() + { + if (!HasPeerId) + { + return this; + } + + var clone = Clone(); + clone.Protocols + .RemoveAll(p => p.Name == "p2p" || p.Name == "ipfs"); + return clone; + } + + /// + /// Writes the binary representation to the specified . + /// + /// + /// The to write to. + /// + /// + /// The binary representation is a sequence of network protocols. + /// + public void Write(Stream stream) + { + var cos = new CodedOutputStream(stream, true); + Write(cos); + cos.Flush(); + } + + /// + /// Writes the binary representation to the specified . + /// + /// + /// The to write to. + /// + /// + /// The binary representation is a sequence of network protocols. + /// + private void Write(CodedOutputStream stream) + { + foreach (var protocol in Protocols) + { + stream.WriteInt64(protocol.Code); + protocol.WriteValue(stream); + } + } + + /// + /// Writes the string representation to the specified . + /// + /// + /// The to write to. + /// + /// + /// The string representation is a sequence of network protocols. + /// + private void Write(TextWriter stream) + { + foreach (var protocol in Protocols) + { + stream.Write('/'); + stream.Write(protocol.Name); + protocol.WriteValue(stream); + } + } + + /// + /// Reads the binary representation of the the specified . + /// + /// + /// The to read from. + /// + /// + /// The binary representation is a sequence of network protocols. + /// + private void Read(Stream stream) { Read(new CodedInputStream(stream, true)); } + + /// + /// Reads the binary representation of the specified . + /// + /// + /// The to read from. + /// + /// + /// The binary representation is a sequence of network protocols. + /// + private void Read(CodedInputStream stream) + { + Protocols.Clear(); + do + { + var code = (uint) stream.ReadInt64(); + if (!NetworkProtocol.Codes.TryGetValue(code, out var protocolType)) + { + throw new InvalidDataException($"The IPFS network protocol code '{code}' is unknown."); + } + + var p = (NetworkProtocol) Activator.CreateInstance(protocolType); + p.ReadValue(stream); + Protocols.Add(p); + } while (!stream.IsAtEnd); + } + + /// + /// Reads the string representation from the specified . + /// + /// + /// The to read from + /// + /// + /// The string representation is a sequence of network protocols. + /// + private void Read(TextReader stream) + { + if (stream.Read() != '/') + { + throw new FormatException("An IPFS multiaddr must start with '/'."); + } + + StringBuilder name = new(); + Protocols.Clear(); + int c; + while (true) + { + name.Clear(); + while (-1 != (c = stream.Read()) && c != '/') + { + name.Append((char) c); + } + + if (name.Length == 0) + { + break; + } + + if (!NetworkProtocol.Names.TryGetValue(name.ToString(), out var protocolType)) + { + throw new FormatException(string.Format("The IPFS network protocol '{0}' is unknown.", + name)); + } + + var p = (NetworkProtocol) Activator.CreateInstance(protocolType); + p.ReadValue(stream); + Protocols.Add(p); + } + + if (Protocols.Count == 0) + { + throw new FormatException("The IFPS multiaddr has no protocol specified."); + } + } + + /// + public override int GetHashCode() + { + var code = 0; + + foreach (var p in Protocols) + { + code += p.Code.GetHashCode(); + code += p.Value?.GetHashCode() ?? 0; + } + + return code; + } + + /// + public override bool Equals(object obj) + { + var that = obj as MultiAddress; + return that != null && Equals(that); + } + + /// + public bool Equals(MultiAddress that) + { + if (Protocols.Count != that.Protocols.Count) + { + return false; + } + + for (var i = 0; i < Protocols.Count; ++i) + { + if (Protocols[i].Code != that.Protocols[i].Code) + { + return false; + } + + if (Protocols[i].Value != that.Protocols[i].Value) + { + return false; + } + } + + return true; + } + + /// + /// Value equality. + /// + public static bool operator ==(MultiAddress a, MultiAddress b) + { + return ReferenceEquals(a, b) || !(a is null) && (!(b is null) && a.Equals(b)); + } + + /// + /// Value inequality. + /// + public static bool operator !=(MultiAddress a, MultiAddress b) + { + return !ReferenceEquals(a, b) && (a is null || (b is null || !a.Equals(b))); + } + + /// + /// A sequence of network protocols that is readable + /// to a human. + /// + public override string ToString() + { + using (StringWriter s = new()) + { + Write(s); + return s.ToString(); + } + } + + /// + /// Returns the IPFS binary representation as a byte array. + /// + /// + /// A byte array. + /// + /// + /// The binary representation is a sequence of network protocols. + /// + public byte[] ToArray() + { + using (MemoryStream ms = new()) + { + Write(ms); + return ms.ToArray(); + } + } + + /// + /// Implicit casting of a to a . + /// + /// The string representation of a . + /// A new . + public static implicit operator MultiAddress(string s) { return new MultiAddress(s); } + + /// + /// Try to create a from the specified + /// string. + /// + /// + /// The string representation of a multi address, such as "/ip4/1270.0.01/tcp/5001". + /// + /// + /// null if the string cannot be parsed; otherwise a . + /// + public static MultiAddress TryCreate(string s) + { + try + { + return new MultiAddress(s); + } + catch + { + return null; + } + } + + /// + /// Try to create a from the specified + /// the binary encoding. + /// + /// + /// The binary encoding of a multiaddress. + /// + /// + /// null if the bytes cannot be parsed; otherwise a . + /// + public static MultiAddress TryCreate(byte[] bytes) + { + try + { + return new MultiAddress(bytes); + } + catch + { + return null; + } + } + + /// + /// Conversion of a to and from JSON. + /// + /// + /// The JSON is just a single string value. + /// + private sealed class Json : JsonConverter + { + public override bool CanConvert(Type objectType) { return true; } + + public override bool CanRead => true; + public override bool CanWrite => true; + + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) + { + var ma = value as MultiAddress; + writer.WriteValue(ma?.ToString()); + } + + public override object ReadJson(JsonReader reader, + Type objectType, + object existingValue, + JsonSerializer serializer) + { + return !(reader.Value is string s) ? null : new MultiAddress(s); + } + } + } +} diff --git a/src/MultiFormats/MultiBase.cs b/src/MultiFormats/MultiBase.cs new file mode 100644 index 0000000000..69600cb25d --- /dev/null +++ b/src/MultiFormats/MultiBase.cs @@ -0,0 +1,123 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using MultiFormats.Registry; + +namespace MultiFormats +{ + /// + /// Self identifying base encodings. + /// + /// + /// MultiBase is a protocol for distinguishing base encodings + /// and other simple string encodings. + /// See the registry for supported algorithms. + /// + /// + public static class MultiBase + { + /// + /// The default multi-base algorithm is "base58btc". + /// + public const string DefaultAlgorithmName = "base58btc"; + + /// + /// Gets the with the specified IPFS multi-hash name. + /// + /// + /// The name of an algorithm, see + /// for + /// for IPFS defined names. + /// + /// + /// When is not registered. + /// + private static MultiBaseAlgorithm GetAlgorithm(string name) + { + try + { + return MultiBaseAlgorithm.Names[name]; + } + catch (KeyNotFoundException) + { + throw new KeyNotFoundException($"MutiBase algorithm '{name}' is not registered."); + } + } + + /// + /// Converts an array of 8-bit unsigned integers to its equivalent string representation. + /// + /// + /// An array of 8-bit unsigned integers. + /// + /// + /// The name of the multi-base algorithm to use. See . + /// + /// + /// A starting with the algorithm's and + /// followed by the encoded string representation of the . + /// + /// + /// When is not registered. + /// + public static string Encode(byte[] bytes, string algorithmName = DefaultAlgorithmName) + { + if (bytes == null) throw new ArgumentNullException(nameof(bytes)); + + var alg = GetAlgorithm(algorithmName); + return alg.Code + alg.Encode(bytes); + } + + /// + /// Converts the specified , which encodes binary data, + /// to an equivalent 8-bit unsigned integer array. + /// + /// + /// The multi-base string to convert. + /// + /// + /// An array of 8-bit unsigned integers that is equivalent to . + /// + /// + /// When the can not be decoded. + /// + public static byte[] Decode(string s) + { + if (string.IsNullOrWhiteSpace(s)) throw new ArgumentNullException(nameof(s)); + + MultiBaseAlgorithm.Codes.TryGetValue(s[0], out var alg); + if (alg == null) throw new FormatException($"MultiBase '{s}' is invalid. The code is not registered."); + + try + { + return alg.Decode(s.Substring(1)); + } + catch (Exception e) + { + throw new FormatException($"MultiBase '{s}' is invalid; decode failed.", e); + } + } + } +} diff --git a/src/MultiFormats/MultiCodec.cs b/src/MultiFormats/MultiCodec.cs new file mode 100644 index 0000000000..07eeec2c85 --- /dev/null +++ b/src/MultiFormats/MultiCodec.cs @@ -0,0 +1,116 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.Collections.Generic; +using System.IO; +using Google.Protobuf; +using MultiFormats.Registry; + +namespace MultiFormats +{ + /// + /// Wraps other formats with a tiny bit of self-description. + /// + /// + /// MultiCodec is a self-describing multiformat, it wraps other formats with a + /// tiny bit of self-description. A multicodec identifier is both a varint and the code + /// identifying the following data. + /// + /// Adds the following extension methods to + /// + /// ReadMultiCodec + /// + /// + /// + /// + /// + /// + public static class MultiCodec + { + /// + /// Reads a from the . + /// + /// + /// A multicodec encoded . + /// + /// The codec. + /// + /// If the code does not exist, a new is + /// registered with the "codec-x"; where + /// 'x' is the code's decimal represention. + /// + public static Codec ReadMultiCodec(this Stream stream) + { + var code = stream.ReadVarint32(); + Codec.Codes.TryGetValue(code, out var codec); + if (codec == null) codec = Codec.Register($"codec-{code}", code); + + return codec; + } + + /// + /// Reads a from the . + /// + /// + /// A multicodec encoded . + /// + /// The codec. + /// + /// If the code does not exist, a new is + /// registered with the "codec-x"; where + /// 'x' is the code's decimal represention. + /// + public static Codec ReadMultiCodec(this CodedInputStream stream) + { + var code = stream.ReadInt32(); + Codec.Codes.TryGetValue(code, out var codec); + if (codec == null) codec = Codec.Register($"codec-{code}", code); + + return codec; + } + + /// + /// Writes a to the . + /// + /// + /// A multicodec encoded . + /// + /// + /// The . + /// + /// + /// Writes the of the to + /// the . + /// + /// + /// When is not registered. + /// + public static void WriteMultiCodec(this Stream stream, string name) + { + Codec.Names.TryGetValue(name, out var codec); + if (codec == null) throw new KeyNotFoundException($"Codec '{name}' is not registered."); + + stream.WriteVarint(codec.Code); + } + } +} diff --git a/src/MultiFormats/MultiFormats.csproj b/src/MultiFormats/MultiFormats.csproj new file mode 100644 index 0000000000..a805b82a83 --- /dev/null +++ b/src/MultiFormats/MultiFormats.csproj @@ -0,0 +1,15 @@ + + + net6.0 + MultiFormats + MultiFormats + + + + + + + + + + diff --git a/src/MultiFormats/MultiHash.cs b/src/MultiFormats/MultiHash.cs new file mode 100644 index 0000000000..2f1cd4c487 --- /dev/null +++ b/src/MultiFormats/MultiHash.cs @@ -0,0 +1,570 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using Common.Logging; +using Google.Protobuf; +using MultiFormats.Registry; +using Newtonsoft.Json; + +namespace MultiFormats +{ + /// + /// A protocol for differentiating outputs from various well-established cryptographic hash functions, + /// addressing size + encoding considerations. + /// + /// + /// See the registry for supported algorithms. + /// + /// + [JsonConverter(typeof(Json))] + public partial class MultiHash : IEquatable + { + private static readonly ILog Log = LogManager.GetLogger(); + + /// + /// The cached base-58 encoding of the multihash. + /// + private string _b58String; + + /// + /// The default hashing algorithm is "sha2-256". + /// + public const string DefaultAlgorithmName = "sha2-256"; + + /// + /// Gets the with the specified IPFS multi-hash name. + /// + /// + /// The name of a hashing algorithm, see + /// for IPFS defined names. + /// + /// + /// The hashing implementation associated with the . + /// After using the hashing algorithm it should be disposed. + /// + /// + /// When is not registered. + /// + public static HashAlgorithm GetHashAlgorithm(string name = DefaultAlgorithmName) + { + try + { + return HashingAlgorithm.Names[name].Hasher(); + } + catch (KeyNotFoundException) + { + throw new KeyNotFoundException($"Hash algorithm '{name}' is not registered."); + } + } + + /// + /// Gets the name of hashing algorithm name with the specified code. + /// + /// + /// The code of a hashing algorithm, see + /// for IPFS defined codes. + /// + /// + /// The name assigned to . + /// + /// + /// When is not registered. + /// + public static string GetHashAlgorithmName(int code) + { + try + { + return HashingAlgorithm.Codes[code].Name; + } + catch (KeyNotFoundException) + { + throw new KeyNotFoundException($"Hash algorithm with code '{code}' is not registered."); + } + } + + /// + /// Occurs when an unknown hashing algorithm number is parsed. + /// + public static EventHandler UnknownHashingAlgorithm; + + /// + /// Creates a new instance of the class with the + /// specified Algorithm name and value. + /// + /// + /// A valid IPFS hashing algorithm name, e.g. "sha2-256" or "sha2-512". + /// + /// + /// The digest value as a byte array. + /// + public MultiHash(string algorithmName, byte[] digest) + { + if (algorithmName == null) + { + throw new ArgumentNullException(nameof(algorithmName)); + } + + if (digest == null) + { + throw new ArgumentNullException(nameof(digest)); + } + + if (!HashingAlgorithm.Names.TryGetValue(algorithmName, out var a)) + { + throw new ArgumentException( + $"The IPFS hashing algorithm '{algorithmName}' is unknown."); + } + + Algorithm = a; + + if (Algorithm.DigestSize != 0 && Algorithm.DigestSize != digest.Length) + { + throw new ArgumentException( + $"The digest size for '{algorithmName}' is {Algorithm.DigestSize} bytes, not {digest.Length}."); + } + + Digest = digest; + } + + /// + /// Creates a new instance of the class from the + /// specified byte array. + /// + /// + /// A sequence of bytes containing the binary representation of the + /// MultiHash. + /// + /// + /// Reads the binary representation of from the . + /// + /// The binary representation is a of the , + /// of the followed by the . + /// + /// + /// When an unknown hashing algorithm number is encountered + /// a new hashing algorithm is registered. This new algorithm does not support + /// matching nor computing a hash. + /// This behaviour allows parsing of any well formed even when + /// the hashing algorithm is unknown. + /// + /// + /// + public MultiHash(byte[] buffer) + { + using (MemoryStream ms = new(buffer, false)) + { + Read(ms); + } + } + + /// + /// Creates a new instance of the class from the + /// specified . + /// + /// + /// A containing the binary representation of the + /// MultiHash. + /// + /// + /// Reads the binary representation of from the . + /// + /// The binary representation is a of the , + /// of the followed by the . + /// + /// + /// When an unknown hashing algorithm number is encountered + /// a new hashing algorithm is registered. This new algorithm does not support + /// matching nor computing a hash. + /// This behaviour allows parsing of any well formed even when + /// the hashing algorithm is unknown. + /// + /// + public MultiHash(Stream stream) { Read(stream); } + + /// + /// Creates a new instance of the class from the + /// specified . + /// + /// + /// A containing the binary representation of the + /// MultiHash. + /// + /// + /// Reads the binary representation of from the . + /// + /// The binary representation is a of the , + /// of the followed by the . + /// + /// + /// When an unknown hashing algorithm number is encountered + /// a new hashing algorithm is registered. This new algorithm does not support + /// matching nor computing a hash. + /// This behaviour allows parsing of any well formed even when + /// the hashing algorithm is unknown. + /// + /// + public MultiHash(CodedInputStream stream) { Read(stream); } + + /// + /// Creates a new instance of the class from the specified + /// encoded . + /// + /// + /// A encoded MultiHash. + /// + /// + /// + /// When an unknown hashing algorithm number is encountered + /// a new hashing algorithm is registered. This new algorithm does not support + /// matching nor computing a hash. + /// This behaviour allows parsing of any well formed even when + /// the hashing algorithm is unknown. + /// + /// + /// + public MultiHash(string s) + { + using (MemoryStream ms = new(s.FromBase58(), false)) + { + Read(ms); + } + } + + /// + /// Implicit casting of a to a . + /// + /// + /// A encoded MultiHash. + /// + /// + /// A new . + /// + /// + /// Equivalent to new MultiHash(s) + /// + public static implicit operator MultiHash(string s) { return new MultiHash(s); } + + /// + /// The hashing algorithm. + /// + /// + /// Details on the hashing algorithm. + /// + public HashingAlgorithm Algorithm { get; private set; } + + /// + /// The hashing algorithm's digest value. + /// + /// + /// The output of the hashing algorithm. + /// + public byte[] Digest { get; private set; } + + /// + /// Determines if the identity hash algorithm is in use. + /// + /// + /// true if the identity hash algorithm is used; otherwise, false. + /// + /// + /// The identity hash is used to inline a small amount of data into a + /// Lib.P2P.Cid + /// + /// . + /// When true, the is also the content. + /// + public bool IsIdentityHash => Algorithm.Code == 0; + + /// + /// Writes the binary representation of the multihash to the specified . + /// + /// + /// The to write to. + /// + /// + /// The binary representation is a 1-byte , + /// 1-byte followed by the . + /// + public void Write(Stream stream) + { + using (CodedOutputStream cos = new(stream, true)) + { + Write(cos); + } + } + + /// + /// Writes the binary representation of the multihash to the specified . + /// + /// + /// The to write to. + /// + /// + /// The binary representation is a of the , + /// of the followed by the . + /// + public void Write(CodedOutputStream stream) + { + if (stream == null) + { + throw new ArgumentNullException(nameof(stream)); + } + + stream.WriteInt32(Algorithm.Code); + stream.WriteLength(Digest.Length); + stream.WriteSomeBytes(Digest); + } + + private void Read(Stream stream) + { + using (CodedInputStream cis = new(stream, true)) + { + Read(cis); + } + } + + private void Read(CodedInputStream stream) + { + var code = stream.ReadInt32(); + var digestSize = stream.ReadLength(); + + HashingAlgorithm.Codes.TryGetValue(code, out var a); + Algorithm = a; + if (Algorithm == null) + { + Algorithm = HashingAlgorithm.Register("ipfs-" + code, code, digestSize); + RaiseUnknownHashingAlgorithm(Algorithm); + } + else if (Algorithm.DigestSize != 0 && digestSize != Algorithm.DigestSize) + { + throw new InvalidDataException( + $"The digest size {digestSize} is wrong for {Algorithm.Name}; it should be {Algorithm.DigestSize}."); + } + + Digest = stream.ReadSomeBytes(digestSize); + } + + /// + public override int GetHashCode() { return ToString().GetHashCode(); } + + /// + public override bool Equals(object obj) + { + var that = obj as MultiHash; + return that != null && Equals(that); + } + + /// + public bool Equals(MultiHash that) + { + return Algorithm.Code == that.Algorithm.Code + && Digest.SequenceEqual(that.Digest); + } + + /// + /// Value equality. + /// + public static bool operator ==(MultiHash a, MultiHash b) + { + return ReferenceEquals(a, b) || !(a is null) && (!(b is null) && a.Equals(b)); + } + + /// + /// Value inequality. + /// + public static bool operator !=(MultiHash a, MultiHash b) { return !(a == b); } + + /// + /// Returns the encoding of the . + /// + /// + /// A base-58 representaton of the MultiHash. + /// + /// + public override string ToString() { return ToBase58(); } + + /// + /// Returns the encoding of the . + /// + /// + /// The representation of the . + /// + public string ToBase58() + { + if (_b58String != null) + { + return _b58String; + } + + using (MemoryStream ms = new()) + { + Write(ms); + _b58String = ms.ToArray().ToBase58(); + return _b58String; + } + } + + /// + /// Returns the encoding of the . + /// + /// + /// The representation of the . + /// + public string ToBase32() { return ToArray().ToBase32(); } + + /// + /// Returns the IPFS binary representation as a byte array. + /// + /// + /// A byte array. + /// + /// + /// The binary representation is a sequence of . + /// + public byte[] ToArray() + { + using (MemoryStream ms = new()) + { + Write(ms); + return ms.ToArray(); + } + } + + /// + /// Determines if the data matches the hash. + /// + /// + /// The data to check. + /// + /// + /// true if the data matches the ; otherwise, false. + /// + /// + /// Matches is used to ensure data integrity. + /// + public bool Matches(byte[] data) + { + var digest = Algorithm.Hasher().ComputeHash(data); + for (var i = digest.Length - 1; 0 <= i; --i) + { + if (digest[i] != Digest[i]) + { + return false; + } + } + + return true; + } + + /// + /// Determines if the stream data matches the hash. + /// + /// + /// The containing the data to check. + /// + /// + /// true if the data matches the ; otherwise, false. + /// + /// + /// Matches is used to ensure data integrity. + /// + public bool Matches(Stream data) + { + var digest = Algorithm.Hasher().ComputeHash(data); + for (var i = digest.Length - 1; 0 <= i; --i) + { + if (digest[i] != Digest[i]) + { + return false; + } + } + + return true; + } + + private void RaiseUnknownHashingAlgorithm(HashingAlgorithm algorithm) + { + if (Log.IsWarnEnabled) + { + Log.WarnFormat("Unknown hashing algorithm number 0x{0:x2}.", algorithm.Code); + } + + var handler = UnknownHashingAlgorithm; + if (handler == null) + { + return; + } + + var args = new UnknownHashingAlgorithmEventArgs + { + Algorithm = algorithm + }; + handler(this, args); + } + + /// + /// Generate the multihash for the specified byte array. + /// + /// + /// The byte array containing the data to hash. + /// + /// + /// The name of the hashing algorithm to use; defaults to . + /// + /// + /// A for the . + /// + public static MultiHash ComputeHash(byte[] data, string algorithmName = DefaultAlgorithmName) + { + using (var alg = GetHashAlgorithm(algorithmName)) + { + return new MultiHash(algorithmName, alg.ComputeHash(data)); + } + } + + /// + /// Generate the multihash for the specified . + /// + /// + /// The containing the data to hash. + /// + /// + /// The name of the hashing algorithm to use; defaults to . + /// + /// + /// A for the . + /// + public static MultiHash ComputeHash(Stream data, string algorithmName = DefaultAlgorithmName) + { + using (var alg = GetHashAlgorithm(algorithmName)) + { + return new MultiHash(algorithmName, alg.ComputeHash(data)); + } + } + } +} diff --git a/src/MultiFormats/NetworkProtocol.cs b/src/MultiFormats/NetworkProtocol.cs new file mode 100644 index 0000000000..60d64e9eb6 --- /dev/null +++ b/src/MultiFormats/NetworkProtocol.cs @@ -0,0 +1,595 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Net; +using System.Net.Sockets; +using Google.Protobuf; + +namespace MultiFormats +{ + /// + /// Metadata on an IPFS network address protocol. + /// + /// + /// Protocols are defined at . + /// + /// + public abstract class NetworkProtocol + { + internal static Dictionary Names = new(); + internal static Dictionary Codes = new(); + + /// + /// Registers the standard network protocols for IPFS. + /// + static NetworkProtocol() + { + Register(); + Register(); + Register(); + Register(); + Register(); + RegisterAlias(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + Register(); + } + + /// + /// Register a network protocol for use. + /// + /// + /// A to register. + /// + public static void Register() where T : NetworkProtocol, new() + { + T protocol = new(); + + if (Names.ContainsKey(protocol.Name)) + { + throw new ArgumentException(string.Format("The IPFS network protocol '{0}' is already defined.", + protocol.Name)); + } + + if (Codes.ContainsKey(protocol.Code)) + { + throw new ArgumentException(string.Format("The IPFS network protocol code ({0}) is already defined.", + protocol.Code)); + } + + Names.Add(protocol.Name, typeof(T)); + Codes.Add(protocol.Code, typeof(T)); + } + + /// + /// Register an alias to another network protocol. + /// + /// + /// A to register. + /// + public static void RegisterAlias() where T : NetworkProtocol, new() + { + T protocol = new(); + + if (Names.ContainsKey(protocol.Name)) + { + throw new ArgumentException($"The IPFS network protocol '{protocol.Name}' is already defined."); + } + + if (!Codes.ContainsKey(protocol.Code)) + { + throw new ArgumentException($"The IPFS network protocol code ({protocol.Code}) is not defined."); + } + + Names.Add(protocol.Name, typeof(T)); + } + + /// + /// The name of the protocol. + /// + public abstract string Name { get; } + + /// + /// The IPFS numeric code assigned to the network protocol. + /// + public abstract uint Code { get; } + + /// + /// The string value associated with the protocol. + /// + /// + /// For tcp and udp this is the port number. This can be null as is the case for http and https. + /// + public string Value { get; set; } + + /// + /// Writes the binary representation to the specified . + /// + /// + /// The to write to. + /// + /// + /// The binary representation of the . + /// + public abstract void WriteValue(CodedOutputStream stream); + + /// + /// Writes the string representation to the specified . + /// + /// + /// The to write to. + /// + /// + /// The string representation of the optional . + /// + public virtual void WriteValue(TextWriter stream) + { + if (Value != null) + { + stream.Write('/'); + stream.Write(Value); + } + } + + /// + /// Reads the binary representation from the specified . + /// + /// + /// The to read from. + /// + /// + /// The binary representation is an option . + /// + public abstract void ReadValue(CodedInputStream stream); + + /// + /// Reads the string representation from the specified . + /// + /// + /// The to read from + /// + /// + /// The string representation is "/" followed by + /// an optional "/". + /// + public virtual void ReadValue(TextReader stream) + { + Value = string.Empty; + int c; + while (-1 != (c = stream.Read()) && c != '/') + { + Value += (char) c; + } + } + + /// + /// The and optional of the network protocol. + /// + public override string ToString() + { + using (StringWriter s = new()) + { + s.Write('/'); + s.Write(Name); + WriteValue(s); + return s.ToString(); + } + } + } + + internal class TcpNetworkProtocol : NetworkProtocol + { + public ushort Port { get; set; } + public override string Name => "tcp"; + public override uint Code => 6; + + public override void ReadValue(TextReader stream) + { + base.ReadValue(stream); + try + { + Port = ushort.Parse(Value); + } + catch (Exception e) + { + throw new FormatException(string.Format("'{0}' is not a valid port number.", Value), e); + } + } + + public override void ReadValue(CodedInputStream stream) + { + var bytes = stream.ReadSomeBytes(2); + Port = (ushort) IPAddress.NetworkToHostOrder(BitConverter.ToInt16(bytes, 0)); + Value = Port.ToString(CultureInfo.InvariantCulture); + } + + public override void WriteValue(CodedOutputStream stream) + { + var bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short) Port)); + stream.WriteSomeBytes(bytes); + } + } + + internal class UdpNetworkProtocol : TcpNetworkProtocol + { + public override string Name => "udp"; + public override uint Code => 273; + } + + internal class DccpNetworkProtocol : TcpNetworkProtocol + { + public override string Name => "dccp"; + public override uint Code => 33; + } + + internal class SctpNetworkProtocol : TcpNetworkProtocol + { + public override string Name => "sctp"; + public override uint Code => 132; + } + + internal abstract class IpNetworkProtocol : NetworkProtocol + { + public IPAddress Address { get; set; } + + public override void ReadValue(TextReader stream) + { + base.ReadValue(stream); + try + { + // Remove the scope id. + var i = Value.LastIndexOf('%'); + if (i != -1) + { + Value = Value.Substring(0, i); + } + + Address = IPAddress.Parse(Value); + } + catch (Exception e) + { + throw new FormatException(string.Format("'{0}' is not a valid IP address.", Value), e); + } + } + + public override void WriteValue(TextWriter stream) + { + stream.Write('/'); + stream.Write(Address.ToString()); + } + + public override void WriteValue(CodedOutputStream stream) + { + var ip = Address.GetAddressBytes(); + stream.WriteSomeBytes(ip); + } + } + + internal class Ipv4NetworkProtocol : IpNetworkProtocol + { + private static readonly int _addressSize = IPAddress.Any.GetAddressBytes().Length; + + public override string Name => "ip4"; + public override uint Code => 4; + + public override void ReadValue(TextReader stream) + { + base.ReadValue(stream); + if (Address.AddressFamily != AddressFamily.InterNetwork) + { + throw new FormatException(string.Format("'{0}' is not a valid IPv4 address.", Value)); + } + } + + public override void ReadValue(CodedInputStream stream) + { + var a = stream.ReadSomeBytes(_addressSize); + Address = new IPAddress(a); + Value = Address.ToString(); + } + } + + internal class Ipv6NetworkProtocol : IpNetworkProtocol + { + private static readonly int _addressSize = IPAddress.IPv6Any.GetAddressBytes().Length; + + public override string Name => "ip6"; + public override uint Code => 41; + + public override void ReadValue(TextReader stream) + { + base.ReadValue(stream); + if (Address.AddressFamily != AddressFamily.InterNetworkV6) + throw new FormatException(string.Format("'{0}' is not a valid IPv6 address.", Value)); + } + + public override void ReadValue(CodedInputStream stream) + { + var a = stream.ReadSomeBytes(_addressSize); + Address = new IPAddress(a); + Value = Address.ToString(); + } + } + + internal class P2PNetworkProtocol : NetworkProtocol + { + public MultiHash MultiHash { get; private set; } + public override string Name => "p2p"; + public override uint Code => 421; + + public override void ReadValue(TextReader stream) + { + base.ReadValue(stream); + MultiHash = new MultiHash(Value); + } + + public override void ReadValue(CodedInputStream stream) + { + stream.ReadLength(); + MultiHash = new MultiHash(stream); + Value = MultiHash.ToBase58(); + } + + public override void WriteValue(CodedOutputStream stream) + { + var bytes = MultiHash.ToArray(); + stream.WriteLength(bytes.Length); + stream.WriteSomeBytes(bytes); + } + } + + internal class IpfsNetworkProtocol : P2PNetworkProtocol + { + public override string Name => "ipfs"; + } + + internal class OnionNetworkProtocol : NetworkProtocol + { + public byte[] Address { get; private set; } + public ushort Port { get; private set; } + public override string Name => "onion"; + public override uint Code => 444; + + public override void ReadValue(TextReader stream) + { + base.ReadValue(stream); + var parts = Value.Split(':'); + if (parts.Length != 2) + throw new FormatException(string.Format("'{0}' is not a valid onion address, missing the port number.", + Value)); + if (parts[0].Length != 16) + throw new FormatException(string.Format("'{0}' is not a valid onion address.", Value)); + try + { + Port = ushort.Parse(parts[1]); + } + catch (Exception e) + { + throw new FormatException( + string.Format("'{0}' is not a valid onion address, invalid port number.", Value), e); + } + + if (Port < 1) + throw new FormatException(string.Format("'{0}' is not a valid onion address, invalid port number.", + Value)); + Address = parts[0].ToUpperInvariant().FromBase32(); + } + + public override void ReadValue(CodedInputStream stream) + { + Address = stream.ReadSomeBytes(10); + var bytes = stream.ReadSomeBytes(2); + Port = (ushort) IPAddress.NetworkToHostOrder(BitConverter.ToInt16(bytes, 0)); + Value = Address.ToBase32().ToLowerInvariant() + ":" + Port.ToString(CultureInfo.InvariantCulture); + } + + public override void WriteValue(CodedOutputStream stream) + { + stream.WriteSomeBytes(Address); + var bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short) Port)); + stream.WriteSomeBytes(bytes); + } + } + + internal abstract class ValuelessNetworkProtocol : NetworkProtocol + { + public override void ReadValue(CodedInputStream stream) + { + // No value to read + } + + public override void ReadValue(TextReader stream) + { + // No value to read + } + + public override void WriteValue(CodedOutputStream stream) + { + // No value to write + } + } + + internal class QuicNetworkProtocol : ValuelessNetworkProtocol + { + public override string Name => "quic"; + public override uint Code => 460; + } + + internal class HttpNetworkProtocol : ValuelessNetworkProtocol + { + public override string Name => "http"; + public override uint Code => 480; + } + + internal class HttpsNetworkProtocol : ValuelessNetworkProtocol + { + public override string Name => "https"; + public override uint Code => 443; + } + + internal class WsNetworkProtocol : ValuelessNetworkProtocol + { + public override string Name => "ws"; + public override uint Code => 477; + } + + internal class WssNetworkProtocol : ValuelessNetworkProtocol + { + public override string Name => "wss"; + public override uint Code => 478; + } + + internal class Libp2PWebrtcStarNetworkProtocol : ValuelessNetworkProtocol + { + public override string Name => "libp2p-webrtc-star"; + public override uint Code => 275; + } + + internal class Libp2PWebrtcDirectNetworkProtocol : ValuelessNetworkProtocol + { + public override string Name => "libp2p-webrtc-direct"; + public override uint Code => 276; + } + + internal class UdtNetworkProtocol : ValuelessNetworkProtocol + { + public override string Name => "udt"; + public override uint Code => 301; + } + + internal class UtpNetworkProtocol : ValuelessNetworkProtocol + { + public override string Name => "utp"; + public override uint Code => 302; + } + + internal class P2PCircuitNetworkProtocol : ValuelessNetworkProtocol + { + public override string Name => "p2p-circuit"; + public override uint Code => 290; + } + + internal abstract class DomainNameNetworkProtocol : NetworkProtocol + { + public string DomainName { get; set; } + + public override void ReadValue(TextReader stream) + { + base.ReadValue(stream); + DomainName = Value; + } + + public override void ReadValue(CodedInputStream stream) + { + Value = stream.ReadString(); + DomainName = Value; + } + + public override void WriteValue(TextWriter stream) + { + stream.Write('/'); + stream.Write(DomainName); + } + + public override void WriteValue(CodedOutputStream stream) { stream.WriteString(DomainName); } + } + + internal class DnsNetworkProtocol : DomainNameNetworkProtocol + { + public override string Name => "dns"; + public override uint Code => 53; + } + + internal class DnsAddrNetworkProtocol : DomainNameNetworkProtocol + { + public override string Name => "dnsaddr"; + public override uint Code => 56; + } + + internal class Dns4NetworkProtocol : DomainNameNetworkProtocol + { + public override string Name => "dns4"; + public override uint Code => 54; + } + + internal class Dns6NetworkProtocol : DomainNameNetworkProtocol + { + public override string Name => "dns6"; + public override uint Code => 55; + } + + internal class IpcidrNetworkProtocol : NetworkProtocol + { + public ushort RoutingPrefix { get; set; } + + public override string Name => "ipcidr"; + + // TODO: https://github.com/multiformats/multiaddr/issues/60 + public override uint Code => 999; + + public override void ReadValue(TextReader stream) + { + base.ReadValue(stream); + try + { + RoutingPrefix = ushort.Parse(Value); + } + catch (Exception e) + { + throw new FormatException(string.Format("'{0}' is not a valid routing prefix.", Value), e); + } + } + + public override void ReadValue(CodedInputStream stream) + { + var bytes = stream.ReadSomeBytes(2); + RoutingPrefix = (ushort) IPAddress.NetworkToHostOrder(BitConverter.ToInt16(bytes, 0)); + Value = RoutingPrefix.ToString(CultureInfo.InvariantCulture); + } + + public override void WriteValue(CodedOutputStream stream) + { + var bytes = BitConverter.GetBytes(IPAddress.HostToNetworkOrder((short) RoutingPrefix)); + stream.WriteSomeBytes(bytes); + } + } +} diff --git a/src/MultiFormats/ProtoBufHelper.cs b/src/MultiFormats/ProtoBufHelper.cs new file mode 100644 index 0000000000..4ae2c7c9de --- /dev/null +++ b/src/MultiFormats/ProtoBufHelper.cs @@ -0,0 +1,104 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Google.Protobuf; +using ProtoBuf; + +namespace MultiFormats +{ + /// + /// Helper methods for ProtoBuf. + /// + public static class ProtoBufHelper + { + private static readonly MethodInfo writeRawBytes = typeof(CodedOutputStream) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) + .Single(m => + m.Name == "WriteRawBytes" && m.GetParameters().Count() == 1 + ); + + private static readonly MethodInfo readRawBytes = typeof(CodedInputStream) + .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) + .Single(m => + m.Name == "ReadRawBytes" + ); + + /// + /// + /// + /// + /// + public static void WriteSomeBytes(this CodedOutputStream stream, byte[] bytes) + { + writeRawBytes.Invoke(stream, new object[] + { + bytes + }); + } + + /// + /// + /// + /// + /// + /// + public static byte[] ReadSomeBytes(this CodedInputStream stream, int length) + { + return (byte[]) readRawBytes.Invoke(stream, new object[] + { + length + }); + } + + /// + /// Read a proto buf message with a varint length prefix. + /// + /// + /// The type of message. + /// + /// + /// The stream containing the message. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result is + /// the message. + /// + public static async Task ReadMessageAsync(Stream stream, CancellationToken cancel = default) + { + var length = await stream.ReadVarint32Async(cancel).ConfigureAwait(false); + var bytes = new byte[length]; + await stream.ReadExactAsync(bytes, 0, length, cancel).ConfigureAwait(false); + + await using MemoryStream ms = new(bytes, false); + return Serializer.Deserialize(ms); + } + } +} diff --git a/src/MultiFormats/Registry/Codec.cs b/src/MultiFormats/Registry/Codec.cs new file mode 100644 index 0000000000..dce97ee828 --- /dev/null +++ b/src/MultiFormats/Registry/Codec.cs @@ -0,0 +1,169 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; + +namespace MultiFormats.Registry +{ + /// + /// Metadata for IPFS multi-codec. + /// + /// + /// IPFS assigns a unique and to codecs. + /// See table.csv + /// for the currently defined multi-codecs. + /// + /// + public class Codec + { + internal static Dictionary Names = new Dictionary(); + internal static Dictionary Codes = new Dictionary(); + + /// + /// Register the standard multi-codecs for IPFS. + /// + /// + static Codec() + { + Register("raw", 0x55); + Register("cms", 0x57); // Not official yet, https://github.com/multiformats/multicodec/pull/69 + Register("cbor", 0x51); + Register("protobuf", 0x50); + Register("rlp", 0x60); + Register("bencode", 0x63); + Register("multicodec", 0x30); + Register("multihash", 0x31); + Register("multiaddr", 0x32); + Register("multibase", 0x33); + Register("dag-pb", 0x70); + Register("dag-cbor", 0x71); + Register("git-raw", 0x78); + Register("eth-block", 0x90); + Register("eth-block-list", 0x91); + Register("eth-tx-trie", 0x92); + Register("eth-tx", 0x93); + Register("eth-tx-receipt-trie", 0x94); + Register("eth-tx-receipt", 0x95); + Register("eth-state-trie", 0x96); + Register("eth-account-snapshot", 0x97); + Register("eth-storage-trie", 0x98); + Register("bitcoin-block", 0xb0); + Register("bitcoin-tx", 0xb1); + Register("zcash-block", 0xc0); + Register("zcash-tx", 0xc1); + Register("stellar-block", 0xd0); + Register("stellar-tx", 0xd1); + Register("torrent-info", 0x7b); + Register("torrent-file", 0x7c); + Register("ed25519-pub", 0xed); + } + + /// + /// The name of the codec. + /// + /// + /// A unique name. + /// + public string Name { get; private set; } + + /// + /// The IPFS code assigned to the codec. + /// + /// + /// Valid codes at . + /// + public int Code { get; private set; } + + /// + /// Use to create a new instance of a . + /// + private Codec() { } + + /// + /// The of the codec. + /// + /// + /// The of the codec. + /// + public override string ToString() { return Name; } + + /// + /// Register a new IPFS codec. + /// + /// + /// The name of the codec. + /// + /// + /// The IPFS code assigned to the codec. + /// + /// + /// A new . + /// + /// + /// When the or is already defined. + /// + /// + /// When the is null or empty. + /// + public static Codec Register(string name, int code) + { + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentNullException(nameof(name)); + if (Names.ContainsKey(name)) + throw new ArgumentException(string.Format("The IPFS codec name '{0}' is already defined.", name)); + if (Codes.ContainsKey(code)) + throw new ArgumentException(string.Format("The IPFS codec code '{0}' is already defined.", code)); + + var a = new Codec + { + Name = name, + Code = code + }; + Names[name] = a; + Codes[code] = a; + + return a; + } + + /// + /// Remove an IPFS codec from the registry. + /// + /// + /// The to remove. + /// + public static void Deregister(Codec codec) + { + Names.Remove(codec.Name); + Codes.Remove(codec.Code); + } + + /// + /// A sequence consisting of all codecs. + /// + /// + /// All the registered codecs. + /// + public static IEnumerable All => Names.Values; + } +} diff --git a/src/MultiFormats/Registry/HashingAlgorithm.cs b/src/MultiFormats/Registry/HashingAlgorithm.cs new file mode 100644 index 0000000000..9e737d781c --- /dev/null +++ b/src/MultiFormats/Registry/HashingAlgorithm.cs @@ -0,0 +1,311 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; +using System.Security.Cryptography; +using MultiFormats.Cryptography; +using BC = Org.BouncyCastle.Crypto.Digests; + +namespace MultiFormats.Registry +{ + /// + /// Metadata and implemetations of a IPFS hashing algorithms. + /// + /// + /// IPFS assigns a unique and to a hashing algorithm. + /// See hashtable.csv + /// for the currently defined hashing algorithms. + /// + /// These algorithms are implemented: + /// + /// blake2b-160, blake2b-256 blake2b-384 and blake2b-512 + /// blake2s-128, blake2s-160, blake2s-224 a nd blake2s-256 + /// keccak-224, keccak-256, keccak-384 and keccak-512 + /// md4 and md5 + /// sha1 + /// sha2-256, sha2-512 and dbl-sha2-256 + /// sha3-224, sha3-256, sha3-384 and sha3-512 + /// shake-128 and shake-256 + /// + /// + /// + /// The identity hash is also implemented; which just returns the input bytes. + /// This is used to inline a small amount of data into a + /// Lib.P2P.Cid + /// + /// . + /// + /// + /// Use to add a new + /// hashing algorithm. + /// + /// + public class HashingAlgorithm + { + internal static Dictionary Names = new Dictionary(); + internal static Dictionary Codes = new Dictionary(); + + /// + /// Register the standard hash algorithms for IPFS. + /// + /// + static HashingAlgorithm() + { + Register("sha1", 0x11, 20, () => SHA1.Create()); + Register("sha2-256", 0x12, 32, () => SHA256.Create()); + Register("sha2-512", 0x13, 64, () => SHA512.Create()); + Register("dbl-sha2-256", 0x56, 32, () => new DoubleSha256()); + Register("keccak-224", 0x1A, 224 / 8, () => new KeccakManaged(224)); + Register("keccak-256", 0x1B, 256 / 8, () => new KeccakManaged(256)); + Register("keccak-384", 0x1C, 384 / 8, () => new KeccakManaged(384)); + Register("keccak-512", 0x1D, 512 / 8, () => new KeccakManaged(512)); + Register("sha3-224", 0x17, 224 / 8, () => new BouncyDigest(new BC.Sha3Digest(224))); + Register("sha3-256", 0x16, 256 / 8, () => new BouncyDigest(new BC.Sha3Digest(256))); + Register("sha3-384", 0x15, 384 / 8, () => new BouncyDigest(new BC.Sha3Digest(384))); + Register("sha3-512", 0x14, 512 / 8, () => new BouncyDigest(new BC.Sha3Digest(512))); + Register("shake-128", 0x18, 128 / 8, () => new BouncyDigest(new BC.ShakeDigest(128))); + Register("shake-256", 0x19, 256 / 8, () => new BouncyDigest(new BC.ShakeDigest(256))); + Register("blake2b-160", 0xb214, 160 / 8, () => new BouncyDigest(new BC.Blake2bDigest(160))); + Register("blake2b-256", 0xb220, 256 / 8, () => new BouncyDigest(new BC.Blake2bDigest(256))); + Register("blake2b-384", 0xb230, 384 / 8, () => new BouncyDigest(new BC.Blake2bDigest(384))); + Register("blake2b-512", 0xb240, 512 / 8, () => new BouncyDigest(new BC.Blake2bDigest(512))); + Register("blake2s-128", 0xb250, 128 / 8, () => new BouncyDigest(new BC.Blake2sDigest(128))); + Register("blake2s-160", 0xb254, 160 / 8, () => new BouncyDigest(new BC.Blake2sDigest(160))); + Register("blake2s-224", 0xb25c, 224 / 8, () => new BouncyDigest(new BC.Blake2sDigest(224))); + Register("blake2s-256", 0xb260, 256 / 8, () => new BouncyDigest(new BC.Blake2sDigest(256))); + Register("md4", 0xd4, 128 / 8, () => new BouncyDigest(new BC.MD4Digest())); + Register("md5", 0xd5, 128 / 8, () => MD5.Create()); + Register("identity", 0, 0, () => new IdentityHash()); + RegisterAlias("id", "identity"); + } + + /// + /// The name of the algorithm. + /// + /// + /// A unique name. + /// + public string Name { get; private set; } + + /// + /// The IPFS number assigned to the hashing algorithm. + /// + /// + /// Valid hash codes at hashtable.csv. + /// + public int Code { get; private set; } + + /// + /// The size, in bytes, of the digest value. + /// + /// + /// The digest value size in bytes. Zero indicates that the digest + /// is non fixed. + /// + public int DigestSize { get; private set; } + + /// + /// Returns a cryptographic hash algorithm that can compute + /// a hash (digest). + /// + public Func Hasher { get; private set; } + + /// + /// Use to create a new instance of a . + /// + private HashingAlgorithm() { } + + /// + /// The of the hashing algorithm. + /// + public override string ToString() { return Name; } + + /// + /// Register a new IPFS hashing algorithm. + /// + /// + /// The name of the algorithm. + /// + /// + /// The IPFS number assigned to the hashing algorithm. + /// + /// + /// The size, in bytes, of the digest value. + /// + /// + /// A Func that returns a . If not specified, then a Func is created to + /// throw a . + /// + /// + /// A new . + /// + public static HashingAlgorithm Register(string name, + int code, + int digestSize, + Func hasher = null) + { + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException(nameof(name)); + } + + if (Names.ContainsKey(name)) + { + throw new ArgumentException(string.Format("The IPFS hashing algorithm '{0}' is already defined.", + name)); + } + + if (Codes.ContainsKey(code)) + { + throw new ArgumentException( + $"The IPFS hashing algorithm code 0x{code:x2} is already defined."); + } + + if (hasher == null) + { + hasher = () => throw new NotImplementedException( + $"The IPFS hashing algorithm '{name}' is not implemented."); + } + + var a = new HashingAlgorithm + { + Name = name, + Code = code, + DigestSize = digestSize, + Hasher = hasher + }; + Names[name] = a; + Codes[code] = a; + + return a; + } + + /// + /// Register an alias for an IPFS hashing algorithm. + /// + /// + /// The alias name. + /// + /// + /// The name of the existing algorithm. + /// + /// + /// A new . + /// + public static HashingAlgorithm RegisterAlias(string alias, string name) + { + if (string.IsNullOrWhiteSpace(alias)) + { + throw new ArgumentNullException(nameof(alias)); + } + + if (Names.ContainsKey(alias)) + { + throw new ArgumentException( + $"The IPFS hashing algorithm '{alias}' is already defined and cannot be used as an alias."); + } + + if (string.IsNullOrWhiteSpace(name)) + { + throw new ArgumentNullException(nameof(name)); + } + + if (!Names.TryGetValue(name, out var existing)) + { + throw new ArgumentException(string.Format("The IPFS hashing algorithm '{0}' is not defined.", name)); + } + + var a = new HashingAlgorithm + { + Name = name, + Code = existing.Code, + DigestSize = existing.DigestSize, + Hasher = existing.Hasher + }; + Names[alias] = a; + + return a; + } + + /// + /// Remove an IPFS hashing algorithm from the registry. + /// + /// + /// The to remove. + /// + public static void DeRegister(HashingAlgorithm algorithm) + { + Names.Remove(algorithm.Name); + Codes.Remove(algorithm.Code); + } + + /// + /// A sequence consisting of all hashing algorithms. + /// + /// + /// The currently registered hashing algorithms. + /// + public static IEnumerable All => Names.Values; + + /// + /// Gets the with the specified IPFS multi-hash name. + /// + /// + /// The name of a hashing algorithm, see + /// for IPFS defined names. + /// + /// + /// The hashing implementation associated with the . + /// + /// + /// When is not registered. + /// + public static HashAlgorithm GetAlgorithm(string name) { return GetAlgorithmMetadata(name).Hasher(); } + + /// + /// Gets the metadata with the specified IPFS multi-hash name. + /// + /// + /// The name of a hashing algorithm, see + /// for IPFS defined names. + /// + /// + /// The metadata associated with the hashing . + /// + /// + /// When is not registered. + /// + public static HashingAlgorithm GetAlgorithmMetadata(string name) + { + try + { + return Names[name]; + } + catch (KeyNotFoundException) + { + throw new KeyNotFoundException($"Hash algorithm '{name}' is not registered."); + } + } + } +} diff --git a/src/MultiFormats/Registry/MultiBaseAlgorithm .cs b/src/MultiFormats/Registry/MultiBaseAlgorithm .cs new file mode 100644 index 0000000000..d91fb4c50d --- /dev/null +++ b/src/MultiFormats/Registry/MultiBaseAlgorithm .cs @@ -0,0 +1,224 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.Collections.Generic; + +namespace MultiFormats.Registry +{ + /// + /// Metadata and implemetations of an IPFS multi-base algorithm. + /// + /// + /// IPFS assigns a unique and to multi-base algorithm. + /// See for + /// the currently defined multi-base algorithms. + /// + /// These algorithms are supported: base58btc, base58flickr, base64, + /// base64pad, base64url, base16, base32, base32z, base32pad, base32hex + /// and base32hexpad. + /// + /// + public class MultiBaseAlgorithm + { + internal static Dictionary Names = new(); + public static Dictionary Codes = new(); + + /// + /// Register the standard multi-base algorithms for IPFS. + /// + /// + static MultiBaseAlgorithm() + { + Register("base58btc", 'z', + bytes => SimpleBase.Base58.Bitcoin.Encode(bytes), + s => SimpleBase.Base58.Bitcoin.Decode(s).ToArray()); + Register("base58flickr", 'Z', + bytes => SimpleBase.Base58.Flickr.Encode(bytes), + s => SimpleBase.Base58.Flickr.Decode(s).ToArray()); + Register("base64", 'm', + bytes => bytes.ToBase64NoPad(), + s => s.FromBase64NoPad()); + Register("base64pad", 'M', + bytes => Convert.ToBase64String(bytes), + s => Convert.FromBase64String(s)); + Register("base64url", 'u', + bytes => bytes.ToBase64Url(), + s => s.FromBase64Url()); + Register("base16", 'f', + bytes => SimpleBase.Base16.EncodeLower(bytes), + s => SimpleBase.Base16.Decode(s).ToArray()); + Register("base32", 'b', + bytes => SimpleBase.Base32.Rfc4648.Encode(bytes, false).ToLowerInvariant(), + s => SimpleBase.Base32.Rfc4648.Decode(s).ToArray()); + Register("base32pad", 'c', + bytes => SimpleBase.Base32.Rfc4648.Encode(bytes, true).ToLowerInvariant(), + s => SimpleBase.Base32.Rfc4648.Decode(s).ToArray()); + Register("base32hex", 'v', + bytes => SimpleBase.Base32.ExtendedHex.Encode(bytes, false).ToLowerInvariant(), + s => SimpleBase.Base32.ExtendedHex.Decode(s).ToArray()); + Register("base32hexpad", 't', + bytes => SimpleBase.Base32.ExtendedHex.Encode(bytes, true).ToLowerInvariant(), + s => SimpleBase.Base32.ExtendedHex.Decode(s).ToArray()); + Register("BASE16", 'F', + bytes => SimpleBase.Base16.EncodeUpper(bytes), + s => SimpleBase.Base16.Decode(s).ToArray()); + Register("BASE32", 'B', + bytes => SimpleBase.Base32.Rfc4648.Encode(bytes, false), + s => SimpleBase.Base32.Rfc4648.Decode(s).ToArray()); + Register("BASE32PAD", 'C', + bytes => SimpleBase.Base32.Rfc4648.Encode(bytes, true), + s => SimpleBase.Base32.Rfc4648.Decode(s).ToArray()); + Register("BASE32HEX", 'V', + bytes => SimpleBase.Base32.ExtendedHex.Encode(bytes, false), + s => SimpleBase.Base32.ExtendedHex.Decode(s).ToArray()); + Register("BASE32HEXPAD", 'T', + bytes => SimpleBase.Base32.ExtendedHex.Encode(bytes, true), + s => SimpleBase.Base32.ExtendedHex.Decode(s).ToArray()); + Register("base32z", 'h', + bytes => Base32Z.Codec.Encode(bytes, false), + s => Base32Z.Codec.Decode(s).ToArray()); + + // Not supported +#if false + Register("base1", '1'); + Register("base2", '0'); + Register("base8", '7'); + Register("base10", '9'); +#endif + } + + /// + /// The name of the algorithm. + /// + /// + /// A unique name. + /// + public string Name { get; private set; } + + /// + /// The IPFS code assigned to the algorithm. + /// + /// + /// Valid codes at . + /// + public char Code { get; private set; } + + /// + /// Returns a function that can return a string from a byte array. + /// + public Func Encode { get; private set; } + + /// + /// Returns a function that can return a byte array from a string. + /// + public Func Decode { get; private set; } + + /// + /// Use to create a new instance of a . + /// + private MultiBaseAlgorithm() { } + + /// + /// The of the algorithm. + /// + public override string ToString() { return Name; } + + /// + /// Register a new IPFS algorithm. + /// + /// + /// The name of the algorithm. + /// + /// + /// The IPFS code assigned to thealgorithm. + /// + /// + /// A Func to encode a byte array. If not specified, then a Func is created to + /// throw a . + /// + /// + /// A Func to decode a string. If not specified, then a Func is created to + /// throw a . + /// + /// + /// A new . + /// + public static MultiBaseAlgorithm Register(string name, + char code, + Func encode = null, + Func decode = null) + { + if (string.IsNullOrWhiteSpace(name)) + throw new ArgumentNullException(nameof(name)); + if (Names.ContainsKey(name)) + throw new ArgumentException( + string.Format("The IPFS multi-base algorithm name '{0}' is already defined.", name)); + if (Codes.ContainsKey(code)) + throw new ArgumentException( + string.Format("The IPFS multi-base algorithm code '{0}' is already defined.", code)); + if (encode == null) + encode = (bytes) => + { + throw new NotImplementedException( + string.Format("The IPFS encode multi-base algorithm '{0}' is not implemented.", name)); + }; + + if (decode == null) + decode = (s) => + { + throw new NotImplementedException( + string.Format("The IPFS decode multi-base algorithm '{0}' is not implemented.", name)); + }; + + var a = new MultiBaseAlgorithm + { + Name = name, + Code = code, + Encode = encode, + Decode = decode + }; + Names[name] = a; + Codes[code] = a; + + return a; + } + + /// + /// Remove an IPFS algorithm from the registry. + /// + /// + /// The to remove. + /// + public static void Deregister(MultiBaseAlgorithm algorithm) + { + Names.Remove(algorithm.Name); + Codes.Remove(algorithm.Code); + } + + /// + /// A sequence consisting of all algorithms. + /// + public static IEnumerable All => Names.Values; + } +} diff --git a/src/MultiFormats/StreamExtensions.cs b/src/MultiFormats/StreamExtensions.cs new file mode 100644 index 0000000000..1b8e1cf8be --- /dev/null +++ b/src/MultiFormats/StreamExtensions.cs @@ -0,0 +1,115 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MultiFormats +{ + /// + /// + public static class StreamExtensions + { + /// + /// Asynchronously reads a sequence of bytes from the stream and advances + /// the position within the stream by the number of bytes read. + /// + /// + /// The stream to read from. + /// + /// + /// The buffer to write the data into. + /// + /// + /// The byte offset in at which to begin + /// writing data from the . + /// + /// + /// The number of bytes to read. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// When the does not have + /// bytes. + /// + public static async Task ReadExactAsync(this Stream stream, byte[] buffer, int offset, int length) + { + while (0 < length) + { + var n = await stream.ReadAsync(buffer, offset, length); + if (n == 0) throw new EndOfStreamException(); + + offset += n; + length -= n; + } + } + + /// + /// Asynchronously reads a sequence of bytes from the stream and advances + /// the position within the stream by the number of bytes read. + /// + /// + /// The stream to read from. + /// + /// + /// The buffer to write the data into. + /// + /// + /// The byte offset in at which to begin + /// writing data from the . + /// + /// + /// The number of bytes to read. + /// + /// + /// Is used to stop the task. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// When the does not have + /// bytes. + /// + public static async Task ReadExactAsync(this Stream stream, + byte[] buffer, + int offset, + int length, + CancellationToken cancel) + { + while (0 < length) + { + var n = await stream.ReadAsync(buffer, offset, length, cancel) + .ConfigureAwait(false); + + if (n == 0) throw new EndOfStreamException(); + + offset += n; + length -= n; + } + } + } +} diff --git a/src/Catalyst.Core.Lib/Repository/PeerEfCoreRepository.cs b/src/MultiFormats/UnknownHashingAlgorithmEventArgs.cs similarity index 62% rename from src/Catalyst.Core.Lib/Repository/PeerEfCoreRepository.cs rename to src/MultiFormats/UnknownHashingAlgorithmEventArgs.cs index 42decaa190..bc163ac1d0 100644 --- a/src/Catalyst.Core.Lib/Repository/PeerEfCoreRepository.cs +++ b/src/MultiFormats/UnknownHashingAlgorithmEventArgs.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -21,16 +21,20 @@ #endregion -using Catalyst.Core.Lib.DAO; -using SharpRepository.EfCoreRepository; -using SharpRepository.Repository.Caching; +using System; +using MultiFormats.Registry; -namespace Catalyst.Core.Lib.Repository +namespace MultiFormats { - public class PeerEfCoreRepository : EfCoreRepository + /// + /// Provides data for the unknown hashing algorithm event. + /// + public class UnknownHashingAlgorithmEventArgs : EventArgs { - public PeerEfCoreRepository(IDbContext dbContext, ICachingStrategy cachingStrategy = null) : - base((Microsoft.EntityFrameworkCore.DbContext) dbContext, cachingStrategy) { } + /// + /// The that is defined for the + /// unknown hashing number. + /// + public HashingAlgorithm Algorithm { get; set; } } } - diff --git a/src/MultiFormats/VarInt.cs b/src/MultiFormats/VarInt.cs new file mode 100644 index 0000000000..82b2c1dc9d --- /dev/null +++ b/src/MultiFormats/VarInt.cs @@ -0,0 +1,287 @@ +#region LICENSE + +/** +* Copyright (c) 2022 Catalyst Network +* +* This file is part of Catalyst.Node +* +* Catalyst.Node is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, either version 2 of the License, or +* (at your option) any later version. +* +* Catalyst.Node is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with Catalyst.Node. If not, see . +*/ + +#endregion + +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace MultiFormats +{ + /// + /// A codec for a variable integer. + /// + /// + /// A VarInt is encoded in network byte order (Big Endian). Each byte (except the last) contains 7 bits + /// of information with the most significant bit set to 1. The last byte has MSB set to 0. + /// + /// Negative values are not allowed. When encountered a is thrown. + /// + /// + /// Adds the following extension methods to + /// + /// + /// + /// + /// + /// + /// + /// + public static class Varint + { + /// + /// Convert the value to its variable integer encoding. + /// + /// + /// The value to convert. + /// + /// + /// A byte array representing the as + /// a variable integer. + /// + public static byte[] Encode(long value) + { + using (var stream = new MemoryStream(64)) + { + stream.WriteVarint(value); + return stream.ToArray(); + } + } + + /// + /// The number of bytes required to encode the value. + /// + /// A positive integer value. + /// + /// The number of bytes required to encode the value. + /// + public static int RequiredBytes(long value) { return Encode(value).Length; } + + /// + /// Convert the byte array to an . + /// + /// + /// A varint encoded byte array containing the variable integer. + /// + /// + /// Offset into to start reading from. + /// + /// The integer value. + public static int DecodeInt32(byte[] bytes, int offset = 0) + { + using (var stream = new MemoryStream(bytes, false)) + { + stream.Position = offset; + return stream.ReadVarint32(); + } + } + + /// + /// Convert the byte array to a . + /// + /// + /// A varint encoded byte array containing the variable integer. + /// + /// + /// Offset into to start reading from. + /// + /// The integer value. + public static long DecodeInt64(byte[] bytes, int offset = 0) + { + using (var stream = new MemoryStream(bytes, false)) + { + stream.Position = offset; + return stream.ReadVarint64(); + } + } + + /// + /// Writes the variable integer encoding of the value to + /// a stream. + /// + /// + /// The to write to. + /// + /// + /// A non-negative value to write. + /// + /// + /// When is negative. + /// + public static void WriteVarint(this Stream stream, long value) + { + stream.WriteVarintAsync(value).ConfigureAwait(false).GetAwaiter().GetResult(); + } + + /// + /// Reads a variable integer from the stream. + /// + /// + /// A varint encoded . + /// + /// + /// When the no bytes exist in the . + /// + /// + /// When the varint value is greater than . + /// + /// The integer value. + public static int ReadVarint32(this Stream stream) + { + return stream.ReadVarint32Async().ConfigureAwait(false).GetAwaiter().GetResult(); + } + + /// + /// Reads a variable integer from the stream. + /// + /// + /// A varint encoded . + /// + /// + /// When the no bytes exist in the . + /// + /// + /// When the varint value is greater than . + /// + /// The integer value. + public static long ReadVarint64(this Stream stream) + { + return stream.ReadVarint64Async().ConfigureAwait(false).GetAwaiter().GetResult(); + } + + /// + /// Writes the variable integer encoding of the value `to + /// a stream. + /// + /// + /// The to write to. + /// + /// + /// A non-negative value to write. + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. + /// + /// + /// When is negative. + /// + public static async Task WriteVarintAsync(this Stream stream, + long value, + CancellationToken cancel = default) + { + if (value < 0) throw new NotSupportedException("Negative values are not allowed for a Varint"); + + var bytes = new byte[10]; + var i = 0; + do + { + var v = (byte) (value & 0x7F); + if (value > 0x7F) v |= 0x80; + + bytes[i++] = v; + value >>= 7; + } while (value != 0); + + await stream.WriteAsync(bytes, 0, i, cancel).ConfigureAwait(false); + } + + /// + /// Reads a variable integer from the stream. + /// + /// + /// A varint encoded . + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the integer value in the . + /// + /// + /// When the no bytes exist in the . + /// + /// + /// When the varint value is greater than . + /// + public static async Task ReadVarint32Async(this Stream stream, + CancellationToken cancel = default) + { + var value = await stream.ReadVarint64Async(cancel).ConfigureAwait(false); + if (value > int.MaxValue) + throw new InvalidDataException("Varint value is bigger than an Int32.MaxValue"); + return (int) value; + } + + /// + /// Reads a variable integer from the stream. + /// + /// + /// A varint encoded . + /// + /// + /// Is used to stop the task. When cancelled, the is raised. + /// + /// + /// When the varint value is greater than . + /// + /// + /// When the no bytes exist in the . + /// + /// + /// A task that represents the asynchronous operation. The task's result + /// is the integer value in the . + /// + public static async Task ReadVarint64Async(this Stream stream, + CancellationToken cancel = default) + { + long value = 0; + var shift = 0; + var bytesRead = 0; + var buffer = new byte[1]; + + while (true) + { + if (1 != await stream.ReadAsync(buffer, 0, 1, cancel).ConfigureAwait(false)) + { + if (bytesRead == 0) + throw new EndOfStreamException(); + + throw new InvalidDataException("Varint is not terminated"); + } + + if (++bytesRead > 9) throw new InvalidDataException("Varint value is bigger than an Int64.MaxValue"); + + var b = buffer[0]; + value |= (long) (b & 0x7F) << shift; + + if (b < 0x80) return value; + + shift += 7; + } + } + } +} diff --git a/src/XunitTraitsRegister.cs b/src/XunitTraitsRegister.cs index ea4c4360cd..008d5b5fe1 100644 --- a/src/XunitTraitsRegister.cs +++ b/src/XunitTraitsRegister.cs @@ -1,7 +1,7 @@ #region LICENSE /** -* Copyright (c) 2019 Catalyst Network +* Copyright (c) 2022 Catalyst Network * * This file is part of Catalyst.Node * @@ -23,7 +23,7 @@ using Catalyst.TestUtils; using FluentAssertions; -using Xunit; +using NUnit.Framework; /// /// This class is only here to fix this issue @@ -36,23 +36,23 @@ /// public class XunitTraitsRegister { - [Fact] - [Trait(Traits.TestType, Traits.IntegrationTest)] + [Test] + public void IntegrationTest() { true.Should().BeTrue(); } - [Fact] - [Trait(Traits.TestType, Traits.EmbeddedChannelTest)] + [Test] + [Category(Traits.EmbeddedChannelTest)] public void EmbeddedChannelTest() { true.Should().BeTrue(); } - [Fact] - [Trait(Traits.TestType, Traits.E2ECosmosDb)] + [Test] + [Category(Traits.E2ECosmosDb)] public void E2E_CosmosDB() { true.Should().BeTrue(); } - [Fact] - [Trait(Traits.TestType, Traits.E2EMongoDb)] + [Test] + [Category(Traits.E2EMongoDb)] public void E2E_MongoDB() { true.Should().BeTrue(); } - [Fact] - [Trait(Traits.TestType, Traits.E2EMssql)] + [Test] + [Category(Traits.E2EMssql)] public void E2E_MSSQL() { true.Should().BeTrue(); } } diff --git a/submodules/Cryptography.FFI.Rust b/submodules/Cryptography.FFI.Rust deleted file mode 160000 index e9aecdc6e0..0000000000 --- a/submodules/Cryptography.FFI.Rust +++ /dev/null @@ -1 +0,0 @@ -Subproject commit e9aecdc6e01076dd7c079ba296448eea0f998efe diff --git a/submodules/Protocol b/submodules/Protocol new file mode 160000 index 0000000000..1a4f95a96e --- /dev/null +++ b/submodules/Protocol @@ -0,0 +1 @@ +Subproject commit 1a4f95a96e161dcef782325c9f2adb0891ae0973 diff --git a/submodules/nethermind b/submodules/nethermind index 27b5df64c5..f1896ee4e6 160000 --- a/submodules/nethermind +++ b/submodules/nethermind @@ -1 +1 @@ -Subproject commit 27b5df64c559f8ebd085d409c05c7917c880e15f +Subproject commit f1896ee4e65cde42f7a18ecca56d722a31b4cca1 diff --git a/submodules/protocol-protobuffs b/submodules/protocol-protobuffs deleted file mode 160000 index 861f51744a..0000000000 --- a/submodules/protocol-protobuffs +++ /dev/null @@ -1 +0,0 @@ -Subproject commit 861f51744a9b69f74e32187b09967234b58809f6