├── .gitignore ├── .nuget └── packages.config ├── IpfsApi.vsmdi ├── IpfsHttpClient.sln ├── LICENSE ├── Local.testsettings ├── README.md ├── TraceAndTestImpact.testsettings ├── appveyor.yml ├── doc ├── .gitignore ├── Documentation.csproj ├── Properties │ └── AssemblyInfo.cs ├── api │ ├── .gitignore │ └── .manifest ├── articles │ ├── async.md │ ├── client.md │ ├── daemon.md │ ├── envvars.md │ ├── filesystem.md │ ├── intro.md │ └── toc.yml ├── docfx.json ├── images │ ├── docs-latest-green.svg │ ├── ipfs-cs-logo-48x48.png │ ├── ipfs-cs-logo-64x64.png │ ├── ipfs-cs-logo.svg │ ├── ipfs-favicon.ico │ └── ipfs-logo.svg ├── index.md ├── template │ ├── fonts │ │ ├── Lato-Bold.eot │ │ ├── Lato-Bold.ttf │ │ ├── Lato-Bold.woff │ │ ├── Lato-Bold.woff2 │ │ ├── Lato-Regular.eot │ │ ├── Lato-Regular.ttf │ │ ├── Lato-Regular.woff │ │ ├── Lato-Regular.woff2 │ │ ├── Lato-Semibold.eot │ │ ├── Lato-Semibold.ttf │ │ ├── Lato-Semibold.woff │ │ ├── Lato-Semibold.woff2 │ │ ├── glyphicons-halflings-regular.eot │ │ ├── glyphicons-halflings-regular.svg │ │ ├── glyphicons-halflings-regular.ttf │ │ ├── glyphicons-halflings-regular.woff │ │ └── glyphicons-halflings-regular.woff2 │ ├── partials │ │ ├── class.header.tmpl.partial │ │ ├── class.tmpl.partial │ │ └── classSubtitle.tmpl.partial │ └── styles │ │ └── main.css └── toc.yml ├── src ├── Block.cs ├── CoreApi │ ├── BitswapApi.cs │ ├── BlockApi.cs │ ├── BlockRepositoryApi.cs │ ├── BootstrapApi.cs │ ├── ConfigApi.cs │ ├── DagApi.cs │ ├── DhtApi.cs │ ├── DnsApi.cs │ ├── FileSystemApi.cs │ ├── GenericApi.cs │ ├── KeyApi.cs │ ├── NameApi.cs │ ├── ObjectApi.cs │ ├── PinApi.cs │ ├── PubSubApi.cs │ ├── StatsApi.cs │ └── SwarmApi.cs ├── FileSystemLink.cs ├── FileSystemNode.cs ├── IpfsClient.cs ├── IpfsHttpClient.csproj ├── MerkleNode.cs ├── PublishedMessage.cs └── TrustedPeerCollection.cs └── test ├── App.config ├── BlockTest.cs ├── CoreApi ├── BitswapApiTest.cs ├── BlockApiTest.cs ├── BlockRepositoryTest.cs ├── BootstrapTest.cs ├── CancellationTest.cs ├── ConfigApiTest.cs ├── DagApiTest.cs ├── DhtApiTest.cs ├── DnsApiTest.cs ├── FileSystemApiTest.cs ├── GenericApiTest.cs ├── KeyApiTest.cs ├── NameApiTest.cs ├── ObjectApiTest.cs ├── PinApiTest.cs ├── PubSubApiTest.cs ├── StatsApiTest.cs └── SwarmApiTest.cs ├── ExceptionAssert.cs ├── FileSystemNodeTest.cs ├── IpfsClientTest.cs ├── IpfsHttpClientTests.csproj ├── MerkleNodeTest.cs ├── PublishedMessageTest.cs ├── TestFixture.cs └── TrustedPeersTest.cs /.gitignore: -------------------------------------------------------------------------------- 1 | ## Ignore Visual Studio temporary files, build results, and 2 | ## files generated by popular Visual Studio add-ons. 3 | 4 | # User-specific files 5 | *.suo 6 | *.user 7 | *.userosscache 8 | *.sln.docstates 9 | 10 | # User-specific files (MonoDevelop/Xamarin Studio) 11 | *.userprefs 12 | 13 | # Build results 14 | [Dd]ebug/ 15 | [Dd]ebugPublic/ 16 | [Rr]elease/ 17 | [Rr]eleases/ 18 | x64/ 19 | x86/ 20 | build/ 21 | bld/ 22 | [Bb]in/ 23 | [Oo]bj/ 24 | 25 | # docfx 26 | api/ 27 | doc/api/ 28 | 29 | # Visual Studo 2015 cache/options directory 30 | .vs/ 31 | 32 | # MSTest test Results 33 | [Tt]est[Rr]esult*/ 34 | [Bb]uild[Ll]og.* 35 | 36 | # NUNIT 37 | *.VisualState.xml 38 | TestResult.xml 39 | 40 | # Build Results of an ATL Project 41 | [Dd]ebugPS/ 42 | [Rr]eleasePS/ 43 | dlldata.c 44 | 45 | *_i.c 46 | *_p.c 47 | *_i.h 48 | *.ilk 49 | *.meta 50 | *.obj 51 | *.pch 52 | *.pdb 53 | *.pgc 54 | *.pgd 55 | *.rsp 56 | *.sbr 57 | *.tlb 58 | *.tli 59 | *.tlh 60 | *.tmp 61 | *.tmp_proj 62 | *.log 63 | *.vspscc 64 | *.vssscc 65 | .builds 66 | *.pidb 67 | *.svclog 68 | *.scc 69 | 70 | # Chutzpah Test files 71 | _Chutzpah* 72 | 73 | # Visual C++ cache files 74 | ipch/ 75 | *.aps 76 | *.ncb 77 | *.opensdf 78 | *.sdf 79 | *.cachefile 80 | 81 | # Visual Studio profiler 82 | *.psess 83 | *.vsp 84 | *.vspx 85 | 86 | # TFS 2012 Local Workspace 87 | $tf/ 88 | 89 | # Guidance Automation Toolkit 90 | *.gpState 91 | 92 | # ReSharper is a .NET coding add-in 93 | _ReSharper*/ 94 | *.[Rr]e[Ss]harper 95 | *.DotSettings.user 96 | 97 | # JustCode is a .NET coding addin-in 98 | .JustCode 99 | 100 | # TeamCity is a build add-in 101 | _TeamCity* 102 | 103 | # DotCover is a Code Coverage Tool 104 | *.dotCover 105 | 106 | # NCrunch 107 | _NCrunch_* 108 | .*crunch*.local.xml 109 | 110 | # MightyMoose 111 | *.mm.* 112 | AutoTest.Net/ 113 | 114 | # Web workbench (sass) 115 | .sass-cache/ 116 | 117 | # Installshield output folder 118 | [Ee]xpress/ 119 | 120 | # DocProject is a documentation generator add-in 121 | DocProject/buildhelp/ 122 | DocProject/Help/*.HxT 123 | DocProject/Help/*.HxC 124 | DocProject/Help/*.hhc 125 | DocProject/Help/*.hhk 126 | DocProject/Help/*.hhp 127 | DocProject/Help/Html2 128 | DocProject/Help/html 129 | 130 | # Click-Once directory 131 | publish/ 132 | 133 | # Publish Web Output 134 | *.[Pp]ublish.xml 135 | *.azurePubxml 136 | # TODO: Comment the next line if you want to checkin your web deploy settings 137 | # but database connection strings (with potential passwords) will be unencrypted 138 | *.pubxml 139 | *.publishproj 140 | 141 | # NuGet Packages 142 | *.nupkg 143 | # The packages folder can be ignored because of Package Restore 144 | **/packages/* 145 | # except build/, which is used as an MSBuild target. 146 | !**/packages/build/ 147 | # Uncomment if necessary however generally it will be regenerated when needed 148 | #!**/packages/repositories.config 149 | 150 | # Windows Azure Build Output 151 | csx/ 152 | *.build.csdef 153 | 154 | # Windows Store app package directory 155 | AppPackages/ 156 | 157 | # Others 158 | *.[Cc]ache 159 | ClientBin/ 160 | [Ss]tyle[Cc]op.* 161 | ~$* 162 | *~ 163 | *.dbmdl 164 | *.dbproj.schemaview 165 | *.pfx 166 | *.publishsettings 167 | node_modules/ 168 | bower_components/ 169 | 170 | # RIA/Silverlight projects 171 | Generated_Code/ 172 | 173 | # Backup & report files from converting an old project file 174 | # to a newer Visual Studio version. Backup files are not needed, 175 | # because we have git ;-) 176 | _UpgradeReport_Files/ 177 | Backup*/ 178 | UpgradeLog*.XML 179 | UpgradeLog*.htm 180 | 181 | # SQL Server files 182 | *.mdf 183 | *.ldf 184 | 185 | # Business Intelligence projects 186 | *.rdl.data 187 | *.bim.layout 188 | *.bim_*.settings 189 | 190 | # Microsoft Fakes 191 | FakesAssemblies/ 192 | 193 | # Node.js Tools for Visual Studio 194 | .ntvs_analysis.dat 195 | 196 | # Visual Studio 6 build log 197 | *.plg 198 | 199 | # Visual Studio 6 workspace options file 200 | *.opt 201 | 202 | # VS 2017 creates this 203 | src/Ipfs.Api.xml 204 | 205 | -------------------------------------------------------------------------------- /.nuget/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /IpfsApi.vsmdi: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /IpfsHttpClient.sln: -------------------------------------------------------------------------------- 1 |  2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio 15 4 | VisualStudioVersion = 15.0.26730.16 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6B9AA9C7-5E90-4D10-9DDE-A2AA5FE3ECA6}" 7 | ProjectSection(SolutionItems) = preProject 8 | .gitignore = .gitignore 9 | appveyor.yml = appveyor.yml 10 | LICENSE = LICENSE 11 | README.md = README.md 12 | EndProjectSection 13 | EndProject 14 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Documentation", "doc\Documentation.csproj", "{F3A32EA9-0B2F-46A3-B47A-33B4C04BD423}" 15 | EndProject 16 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpfsHttpClient", "src\IpfsHttpClient.csproj", "{F3C81C57-C283-4E07-B765-DEABCFB22136}" 17 | EndProject 18 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpfsHttpClientTests", "test\IpfsHttpClientTests.csproj", "{B459FBC7-4A28-4170-AD83-7348A403407F}" 19 | EndProject 20 | Global 21 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 22 | Debug|Any CPU = Debug|Any CPU 23 | Release|Any CPU = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 26 | {F3A32EA9-0B2F-46A3-B47A-33B4C04BD423}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 27 | {F3A32EA9-0B2F-46A3-B47A-33B4C04BD423}.Release|Any CPU.ActiveCfg = Release|Any CPU 28 | {F3C81C57-C283-4E07-B765-DEABCFB22136}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 29 | {F3C81C57-C283-4E07-B765-DEABCFB22136}.Debug|Any CPU.Build.0 = Debug|Any CPU 30 | {F3C81C57-C283-4E07-B765-DEABCFB22136}.Release|Any CPU.ActiveCfg = Release|Any CPU 31 | {F3C81C57-C283-4E07-B765-DEABCFB22136}.Release|Any CPU.Build.0 = Release|Any CPU 32 | {B459FBC7-4A28-4170-AD83-7348A403407F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 33 | {B459FBC7-4A28-4170-AD83-7348A403407F}.Debug|Any CPU.Build.0 = Debug|Any CPU 34 | {B459FBC7-4A28-4170-AD83-7348A403407F}.Release|Any CPU.ActiveCfg = Release|Any CPU 35 | {B459FBC7-4A28-4170-AD83-7348A403407F}.Release|Any CPU.Build.0 = Release|Any CPU 36 | EndGlobalSection 37 | GlobalSection(SolutionProperties) = preSolution 38 | HideSolutionNode = FALSE 39 | EndGlobalSection 40 | GlobalSection(ExtensibilityGlobals) = postSolution 41 | SolutionGuid = {36ED5AA7-8F41-4F7D-A665-230635EF64A1} 42 | EndGlobalSection 43 | EndGlobal 44 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Richard Schneider 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /Local.testsettings: -------------------------------------------------------------------------------- 1 |  2 | 3 | These are default test settings for a local test run. 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # net-ipfs-http-client 2 | 3 | [![build status](https://ci.appveyor.com/api/projects/status/github/richardschneider/net-ipfs-http-client?branch=master&svg=true)](https://ci.appveyor.com/project/richardschneider/net-ipfs-http-client) 4 | [![Coverage Status](https://coveralls.io/repos/github/richardschneider/net-ipfs-http-client/badge.svg?branch=master)](https://coveralls.io/github/richardschneider/net-ipfs-http-client?branch=master) 5 | [![Version](https://img.shields.io/nuget/v/Ipfs.Http.Client.svg)](https://www.nuget.org/packages/Ipfs.Http.Client) 6 | [![docs](https://img.shields.io/badge/docs-latest-green.svg)](https://richardschneider.github.io/net-ipfs-http-client/articles/client.html) 7 | 8 | 9 | A .Net client library for managing IPFS using the [HTTP API](https://docs.ipfs.io/reference/api/http/) protocol. 10 | More information, including the Class Reference, is on the [Project](https://richardschneider.github.io/net-ipfs-http-client/) web site. 11 | 12 | ![](https://ipfs.io/ipfs/QmQJ68PFMDdAsgCZvA1UVzzn18asVcf7HVvCDgpjiSCAse) 13 | 14 | ## Features 15 | 16 | - Targets .NET Framework 4.5, .NET Standard 1.4 and .NET Standard 2.0 17 | - [Asynchronous I/O](https://richardschneider.github.io/net-ipfs-http-client/articles/async.html) to an IPFS server 18 | - Supports [cancellation](https://richardschneider.github.io/net-ipfs-http-client/articles/cancellation.html) of all requests to the IPFS Server 19 | - Requests that all responses are compressed 20 | - Comprehensive [documentation](https://richardschneider.github.io/net-ipfs-http-client/articles/client.html) 21 | - C# style access to the [ipfs core interface](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.html) 22 | - [Bitswap API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IBitswapApi.html) 23 | - [Block API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IBlockApi.html) 24 | - [Config API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IConfigApi.html) 25 | - [Dag API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IDagApi.html) 26 | - [Dht API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IDhtApi.html) 27 | - [Misc API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IGenericApi.html) 28 | - [FileSystem API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IFileSystemApi.html) 29 | - [Key API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IKeyApi.html) 30 | - [Name API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.INameApi.html) 31 | - [Object API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IObjectApi.html) 32 | - [Pin API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IPinApi.html) 33 | - [PubSub API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IPubSubApi.html) 34 | - [Stats API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.IStatsApi.html) 35 | - [Swarm API](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.CoreApi.ISwarmApi.html) 36 | 37 | ## Getting started 38 | 39 | Published releases of IPFS API are available on [NuGet](https://www.nuget.org/packages/ipfs.http.client/). To install, run the following command in the [Package Manager Console](https://docs.nuget.org/docs/start-here/using-the-package-manager-console). 40 | 41 | PM> Install-Package Ipfs.Http.Client 42 | 43 | Or using [dotnet](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet?tabs=netcore21) 44 | 45 | > dotnet add package Ipfs.Http.Client 46 | 47 | ## IpfsClient 48 | 49 | Every feature of IPFS is a property of the [IpfsClient](https://richardschneider.github.io/net-ipfs-http-client/api/Ipfs.Http.IpfsClient.html). The following example 50 | uses `FileSystem` to read a text file 51 | 52 | ```csharp 53 | using Ipfs.Http; 54 | 55 | var ipfs = new IpfsClient(); 56 | 57 | const string filename = "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about"; 58 | string text = await ipfs.FileSystem.ReadAllTextAsync(filename); 59 | ``` 60 | 61 | # License 62 | Copyright © 2015-2018 Richard Schneider (makaretu@gmail.com) 63 | 64 | The IPFS API library 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/net-ipfs-http-client/blob/master/LICENSE) file for more information. 65 | 66 | Buy Me A Coffee 67 | -------------------------------------------------------------------------------- /TraceAndTestImpact.testsettings: -------------------------------------------------------------------------------- 1 |  2 | 3 | These are test settings for Trace and Test Impact. 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /appveyor.yml: -------------------------------------------------------------------------------- 1 | # gitversion will change the version number 2 | version: x-{build} 3 | 4 | for: 5 | - 6 | branches: 7 | only: 8 | - master 9 | 10 | environment: 11 | git_token: 12 | secure: NeX5NCOUXsCLc1UjTJjqB9F02FZ8Wq0VsxqTXC8kBdyK6zjxjebrf/9Da2sY1Kql 13 | snk_secret: 14 | secure: 5QzEIgiDqTIrZruPaIQIvTlNMl5BZ7TGEps7ALyBfHE= 15 | 16 | configuration: Release 17 | os: Visual Studio 2017 18 | 19 | init: 20 | - git config --global core.autocrlf input 21 | - git config --global credential.helper store 22 | - ps: Add-Content "$env:USERPROFILE\.git-credentials" "https://$($env:git_token):x-oauth-basic@github.com`n" 23 | - git config --global user.email "noreply@emanon.org" 24 | - git config --global user.name "Appveyor CI" 25 | 26 | environment: 27 | COVERALLS_REPO_TOKEN: 28 | secure: u109t00NLIZQyREMNUyPlMd8gCi6w3o790yT8mQnzEC0LvZyevt+04u63TQfrifC 29 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 30 | 31 | # tools we need for bulding/testing/deploying 32 | install: 33 | - dotnet --version 34 | - choco install gitversion.portable -y 35 | - npm install gh-pages -g 36 | 37 | # - nuget install secure-file -ExcludeVersion 38 | # - if defined snk_secret secure-file\tools\secure-file -decrypt src\ipfs.ci.snk.enc -secret %snk_secret% -out src\ipfs.dev.snk 39 | # - choco install go-ipfs 40 | - choco install go-ipfs --version 0.4.21 --force 41 | - ipfs init 42 | - ps: Start-Process -FilePath "ipfs.exe" -ArgumentList "daemon --enable-pubsub-experiment" 43 | 44 | # gitversion will change the assembly info 45 | pull_requests: 46 | do_not_increment_build_number: true 47 | 48 | before_build: 49 | - nuget restore 50 | - ps: gitversion /output buildserver /updateAssemblyInfo >gitversion.log 51 | 52 | build_script: 53 | - dotnet build -c %CONFIGURATION% -p:Version=%GitVersion_MajorMinorPatch% -p:AssemblyVersion=%GitVersion_MajorMinorPatch% 54 | - dotnet pack -c %CONFIGURATION% --no-build --no-restore -p:Version=%GitVersion_MajorMinorPatch% -p:AssemblyVersion=%GitVersion_MajorMinorPatch% 55 | 56 | after_build: 57 | - cmd: appveyor PushArtifact "src\bin\%CONFIGURATION%\Ipfs.Http.Client.%GitVersion_MajorMinorPatch%.nupkg" 58 | 59 | # Build documentation in doc\_site 60 | # v2.43 is requiring VS 2019!!! 61 | - cmd: choco install docfx -y --version 2.42 --force 62 | - docfx doc\docfx.json --logLevel Warning --warningsAsErrors 63 | - 7z a -tzip docs.zip doc\_site 64 | - appveyor PushArtifact docs.zip 65 | - if defined git_token gh-pages -d doc\_site -m "new docs %GitVersion_FullSemVer%" 66 | 67 | test_script: 68 | - dotnet test -c %CONFIGURATION% --no-build --no-restore test 69 | 70 | after_test: 71 | # Generate coverage report 72 | - packages\OpenCover.4.6.519\tools\OpenCover.Console.exe 73 | -register:user -filter:"+[Ipfs.Http*]* -[*Tests]*" 74 | -target:"c:\Program Files\dotnet\dotnet.exe" 75 | -targetargs:"test -c Release --no-build --no-restore --framework netcoreapp2.0 test" 76 | -output:coverage.xml 77 | -mergeoutput 78 | -hideskipped:File 79 | -oldStyle 80 | - if defined COVERALLS_REPO_TOKEN 81 | packages\coveralls.net.0.6.0\tools\csmacnz.coveralls.exe 82 | --opencover -i ./coverage.xml --useRelativePaths --serviceName appveyor --jobId %APPVEYOR_BUILD_NUMBER% 83 | 84 | # publish NuGet package on tag build 85 | nuget: 86 | disable_publish_on_pr: true 87 | 88 | deploy: 89 | - provider: NuGet 90 | api_key: 91 | secure: OdmGEj/l0K0ZPDmXAYx+fryCzV012eTrM29ALBuL0waxvwLvrufdDXiI+1iNhWEG 92 | on: 93 | appveyor_repo_tag: true 94 | -------------------------------------------------------------------------------- /doc/.gitignore: -------------------------------------------------------------------------------- 1 | ############### 2 | # folder # 3 | ############### 4 | /**/DROP/ 5 | /**/TEMP/ 6 | /**/packages/ 7 | /**/bin/ 8 | /**/obj/ 9 | _site 10 | log.txt 11 | msdn.4.5.2.zip 12 | namespaces.4.5.2.zip 13 | 14 | -------------------------------------------------------------------------------- /doc/Documentation.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard2 5 | 6 | 7 | -------------------------------------------------------------------------------- /doc/Properties/AssemblyInfo.cs: -------------------------------------------------------------------------------- 1 | using System.Reflection; 2 | using System.Runtime.CompilerServices; 3 | using System.Runtime.InteropServices; 4 | 5 | // General Information about an assembly is controlled through the following 6 | // set of attributes. Change these attribute values to modify the information 7 | // associated with an assembly. 8 | [assembly: AssemblyTitle("WebApplication1")] 9 | [assembly: AssemblyDescription("")] 10 | [assembly: AssemblyConfiguration("")] 11 | [assembly: AssemblyCompany("")] 12 | [assembly: AssemblyProduct("WebApplication1")] 13 | [assembly: AssemblyCopyright("Copyright © 2016")] 14 | [assembly: AssemblyTrademark("")] 15 | [assembly: AssemblyCulture("")] 16 | 17 | // Setting ComVisible to false makes the types in this assembly not visible 18 | // to COM components. If you need to access a type in this assembly from 19 | // COM, set the ComVisible attribute to true on that type. 20 | [assembly: ComVisible(false)] 21 | 22 | // The following GUID is for the ID of the typelib if this project is exposed to COM 23 | [assembly: Guid("f3a32ea9-0b2f-46a3-b47a-33b4c04bd423")] 24 | 25 | // Version information for an assembly consists of the following four values: 26 | // 27 | // Major Version 28 | // Minor Version 29 | // Build Number 30 | // Revision 31 | // 32 | // You can specify all the values or you can default the Revision and Build Numbers 33 | // by using the '*' as shown below: 34 | [assembly: AssemblyVersion("1.0.0.0")] 35 | [assembly: AssemblyFileVersion("1.0.0.0")] 36 | -------------------------------------------------------------------------------- /doc/api/.gitignore: -------------------------------------------------------------------------------- 1 | ############### 2 | # temp file # 3 | ############### 4 | *.yml 5 | -------------------------------------------------------------------------------- /doc/api/.manifest: -------------------------------------------------------------------------------- 1 | { 2 | "Ipfs.Http": "Ipfs.Http.yml", 3 | "Ipfs.Http.Block": "Ipfs.Http.Block.yml", 4 | "Ipfs.Http.Block.DataBytes": "Ipfs.Http.Block.yml", 5 | "Ipfs.Http.Block.DataStream": "Ipfs.Http.Block.yml", 6 | "Ipfs.Http.Block.Id": "Ipfs.Http.Block.yml", 7 | "Ipfs.Http.Block.Size": "Ipfs.Http.Block.yml", 8 | "Ipfs.Http.FileSystemLink": "Ipfs.Http.FileSystemLink.yml", 9 | "Ipfs.Http.FileSystemLink.Id": "Ipfs.Http.FileSystemLink.yml", 10 | "Ipfs.Http.FileSystemLink.IsDirectory": "Ipfs.Http.FileSystemLink.yml", 11 | "Ipfs.Http.FileSystemLink.Name": "Ipfs.Http.FileSystemLink.yml", 12 | "Ipfs.Http.FileSystemLink.Size": "Ipfs.Http.FileSystemLink.yml", 13 | "Ipfs.Http.FileSystemNode": "Ipfs.Http.FileSystemNode.yml", 14 | "Ipfs.Http.FileSystemNode.DataBytes": "Ipfs.Http.FileSystemNode.yml", 15 | "Ipfs.Http.FileSystemNode.DataStream": "Ipfs.Http.FileSystemNode.yml", 16 | "Ipfs.Http.FileSystemNode.Id": "Ipfs.Http.FileSystemNode.yml", 17 | "Ipfs.Http.FileSystemNode.IpfsClient": "Ipfs.Http.FileSystemNode.yml", 18 | "Ipfs.Http.FileSystemNode.IsDirectory": "Ipfs.Http.FileSystemNode.yml", 19 | "Ipfs.Http.FileSystemNode.Links": "Ipfs.Http.FileSystemNode.yml", 20 | "Ipfs.Http.FileSystemNode.Name": "Ipfs.Http.FileSystemNode.yml", 21 | "Ipfs.Http.FileSystemNode.Size": "Ipfs.Http.FileSystemNode.yml", 22 | "Ipfs.Http.FileSystemNode.ToLink(System.String)": "Ipfs.Http.FileSystemNode.yml", 23 | "Ipfs.Http.IpfsClient": "Ipfs.Http.IpfsClient.yml", 24 | "Ipfs.Http.IpfsClient.#ctor": "Ipfs.Http.IpfsClient.yml", 25 | "Ipfs.Http.IpfsClient.#ctor(System.String)": "Ipfs.Http.IpfsClient.yml", 26 | "Ipfs.Http.IpfsClient.ApiUri": "Ipfs.Http.IpfsClient.yml", 27 | "Ipfs.Http.IpfsClient.Bitswap": "Ipfs.Http.IpfsClient.yml", 28 | "Ipfs.Http.IpfsClient.Block": "Ipfs.Http.IpfsClient.yml", 29 | "Ipfs.Http.IpfsClient.BlockRepository": "Ipfs.Http.IpfsClient.yml", 30 | "Ipfs.Http.IpfsClient.Bootstrap": "Ipfs.Http.IpfsClient.yml", 31 | "Ipfs.Http.IpfsClient.Config": "Ipfs.Http.IpfsClient.yml", 32 | "Ipfs.Http.IpfsClient.Dag": "Ipfs.Http.IpfsClient.yml", 33 | "Ipfs.Http.IpfsClient.DefaultApiUri": "Ipfs.Http.IpfsClient.yml", 34 | "Ipfs.Http.IpfsClient.Dht": "Ipfs.Http.IpfsClient.yml", 35 | "Ipfs.Http.IpfsClient.Dns": "Ipfs.Http.IpfsClient.yml", 36 | "Ipfs.Http.IpfsClient.DoCommandAsync(System.String,System.Threading.CancellationToken,System.String,System.String[])": "Ipfs.Http.IpfsClient.yml", 37 | "Ipfs.Http.IpfsClient.DoCommandAsync``1(System.String,System.Threading.CancellationToken,System.String,System.String[])": "Ipfs.Http.IpfsClient.yml", 38 | "Ipfs.Http.IpfsClient.DownloadAsync(System.String,System.Threading.CancellationToken,System.String,System.String[])": "Ipfs.Http.IpfsClient.yml", 39 | "Ipfs.Http.IpfsClient.DownloadBytesAsync(System.String,System.Threading.CancellationToken,System.String,System.String[])": "Ipfs.Http.IpfsClient.yml", 40 | "Ipfs.Http.IpfsClient.FileSystem": "Ipfs.Http.IpfsClient.yml", 41 | "Ipfs.Http.IpfsClient.Generic": "Ipfs.Http.IpfsClient.yml", 42 | "Ipfs.Http.IpfsClient.IdAsync(Ipfs.MultiHash,System.Threading.CancellationToken)": "Ipfs.Http.IpfsClient.yml", 43 | "Ipfs.Http.IpfsClient.Key": "Ipfs.Http.IpfsClient.yml", 44 | "Ipfs.Http.IpfsClient.Name": "Ipfs.Http.IpfsClient.yml", 45 | "Ipfs.Http.IpfsClient.Object": "Ipfs.Http.IpfsClient.yml", 46 | "Ipfs.Http.IpfsClient.Pin": "Ipfs.Http.IpfsClient.yml", 47 | "Ipfs.Http.IpfsClient.PostDownloadAsync(System.String,System.Threading.CancellationToken,System.String,System.String[])": "Ipfs.Http.IpfsClient.yml", 48 | "Ipfs.Http.IpfsClient.PubSub": "Ipfs.Http.IpfsClient.yml", 49 | "Ipfs.Http.IpfsClient.ResolveAsync(System.String,System.Boolean,System.Threading.CancellationToken)": "Ipfs.Http.IpfsClient.yml", 50 | "Ipfs.Http.IpfsClient.ShutdownAsync": "Ipfs.Http.IpfsClient.yml", 51 | "Ipfs.Http.IpfsClient.Stats": "Ipfs.Http.IpfsClient.yml", 52 | "Ipfs.Http.IpfsClient.Swarm": "Ipfs.Http.IpfsClient.yml", 53 | "Ipfs.Http.IpfsClient.TrustedPeers": "Ipfs.Http.IpfsClient.yml", 54 | "Ipfs.Http.IpfsClient.Upload2Async(System.String,System.Threading.CancellationToken,System.IO.Stream,System.String,System.String[])": "Ipfs.Http.IpfsClient.yml", 55 | "Ipfs.Http.IpfsClient.UploadAsync(System.String,System.Threading.CancellationToken,System.Byte[],System.String[])": "Ipfs.Http.IpfsClient.yml", 56 | "Ipfs.Http.IpfsClient.UploadAsync(System.String,System.Threading.CancellationToken,System.IO.Stream,System.String,System.String[])": "Ipfs.Http.IpfsClient.yml", 57 | "Ipfs.Http.IpfsClient.UserAgent": "Ipfs.Http.IpfsClient.yml", 58 | "Ipfs.Http.IpfsClient.VersionAsync(System.Threading.CancellationToken)": "Ipfs.Http.IpfsClient.yml", 59 | "Ipfs.Http.MerkleNode": "Ipfs.Http.MerkleNode.yml", 60 | "Ipfs.Http.MerkleNode.#ctor(Ipfs.Cid,System.String)": "Ipfs.Http.MerkleNode.yml", 61 | "Ipfs.Http.MerkleNode.#ctor(Ipfs.IMerkleLink)": "Ipfs.Http.MerkleNode.yml", 62 | "Ipfs.Http.MerkleNode.#ctor(System.String,System.String)": "Ipfs.Http.MerkleNode.yml", 63 | "Ipfs.Http.MerkleNode.BlockSize": "Ipfs.Http.MerkleNode.yml", 64 | "Ipfs.Http.MerkleNode.DataBytes": "Ipfs.Http.MerkleNode.yml", 65 | "Ipfs.Http.MerkleNode.DataStream": "Ipfs.Http.MerkleNode.yml", 66 | "Ipfs.Http.MerkleNode.Equals(Ipfs.Http.MerkleNode)": "Ipfs.Http.MerkleNode.yml", 67 | "Ipfs.Http.MerkleNode.Equals(System.Object)": "Ipfs.Http.MerkleNode.yml", 68 | "Ipfs.Http.MerkleNode.GetHashCode": "Ipfs.Http.MerkleNode.yml", 69 | "Ipfs.Http.MerkleNode.Id": "Ipfs.Http.MerkleNode.yml", 70 | "Ipfs.Http.MerkleNode.Links": "Ipfs.Http.MerkleNode.yml", 71 | "Ipfs.Http.MerkleNode.Name": "Ipfs.Http.MerkleNode.yml", 72 | "Ipfs.Http.MerkleNode.op_Equality(Ipfs.Http.MerkleNode,Ipfs.Http.MerkleNode)": "Ipfs.Http.MerkleNode.yml", 73 | "Ipfs.Http.MerkleNode.op_Implicit(System.String)~Ipfs.Http.MerkleNode": "Ipfs.Http.MerkleNode.yml", 74 | "Ipfs.Http.MerkleNode.op_Inequality(Ipfs.Http.MerkleNode,Ipfs.Http.MerkleNode)": "Ipfs.Http.MerkleNode.yml", 75 | "Ipfs.Http.MerkleNode.Size": "Ipfs.Http.MerkleNode.yml", 76 | "Ipfs.Http.MerkleNode.ToLink(System.String)": "Ipfs.Http.MerkleNode.yml", 77 | "Ipfs.Http.MerkleNode.ToString": "Ipfs.Http.MerkleNode.yml", 78 | "Ipfs.Http.PublishedMessage": "Ipfs.Http.PublishedMessage.yml", 79 | "Ipfs.Http.PublishedMessage.#ctor(System.String)": "Ipfs.Http.PublishedMessage.yml", 80 | "Ipfs.Http.PublishedMessage.DataBytes": "Ipfs.Http.PublishedMessage.yml", 81 | "Ipfs.Http.PublishedMessage.DataStream": "Ipfs.Http.PublishedMessage.yml", 82 | "Ipfs.Http.PublishedMessage.DataString": "Ipfs.Http.PublishedMessage.yml", 83 | "Ipfs.Http.PublishedMessage.Id": "Ipfs.Http.PublishedMessage.yml", 84 | "Ipfs.Http.PublishedMessage.Sender": "Ipfs.Http.PublishedMessage.yml", 85 | "Ipfs.Http.PublishedMessage.SequenceNumber": "Ipfs.Http.PublishedMessage.yml", 86 | "Ipfs.Http.PublishedMessage.Size": "Ipfs.Http.PublishedMessage.yml", 87 | "Ipfs.Http.PublishedMessage.Topics": "Ipfs.Http.PublishedMessage.yml", 88 | "Ipfs.Http.TrustedPeerCollection": "Ipfs.Http.TrustedPeerCollection.yml", 89 | "Ipfs.Http.TrustedPeerCollection.Add(Ipfs.MultiAddress)": "Ipfs.Http.TrustedPeerCollection.yml", 90 | "Ipfs.Http.TrustedPeerCollection.AddDefaultNodes": "Ipfs.Http.TrustedPeerCollection.yml", 91 | "Ipfs.Http.TrustedPeerCollection.Clear": "Ipfs.Http.TrustedPeerCollection.yml", 92 | "Ipfs.Http.TrustedPeerCollection.Contains(Ipfs.MultiAddress)": "Ipfs.Http.TrustedPeerCollection.yml", 93 | "Ipfs.Http.TrustedPeerCollection.CopyTo(Ipfs.MultiAddress[],System.Int32)": "Ipfs.Http.TrustedPeerCollection.yml", 94 | "Ipfs.Http.TrustedPeerCollection.Count": "Ipfs.Http.TrustedPeerCollection.yml", 95 | "Ipfs.Http.TrustedPeerCollection.GetEnumerator": "Ipfs.Http.TrustedPeerCollection.yml", 96 | "Ipfs.Http.TrustedPeerCollection.IsReadOnly": "Ipfs.Http.TrustedPeerCollection.yml", 97 | "Ipfs.Http.TrustedPeerCollection.Remove(Ipfs.MultiAddress)": "Ipfs.Http.TrustedPeerCollection.yml", 98 | "Ipfs.Http.TrustedPeerCollection.System#Collections#IEnumerable#GetEnumerator": "Ipfs.Http.TrustedPeerCollection.yml" 99 | } -------------------------------------------------------------------------------- /doc/articles/async.md: -------------------------------------------------------------------------------- 1 | # Asynchronous I/O 2 | 3 | All requests to the IPFS server are [asynchronous](https://docs.microsoft.com/en-us/dotnet/csharp/async), 4 | which does not block current thread. 5 | 6 | This means that callers should **normally** use the `async/await` paradigm 7 | 8 | ```cs 9 | var result = await ipfs.FileSystem.AddTextAsync("I am pinned"); 10 | ``` 11 | 12 | ## Synchronous 13 | 14 | If a synchronous operation is required, then this can work 15 | 16 | ```cs 17 | var result = ipfs.FileSystem.AddTextAsync("I am pinned").Result; 18 | ``` 19 | 20 | ## Cancelling a request 21 | 22 | All requests to the IPFS server can be cancelled by supplying 23 | an optional [CancellationToken](xref:System.Threading.CancellationToken). When 24 | the token is cancelled, 25 | a [TaskCanceledException](xref:System.Threading.Tasks.TaskCanceledException) 26 | will be `thrown`. 27 | 28 | Here's a contrived example ([unit test](https://github.com/richardschneider/net-ipfs-http-client/blob/cancellation/test/CoreApi/CancellationTest.cs)) 29 | that forces the getting of info on the local IPFS server to be cancelled 30 | 31 | ```csharp 32 | var cts = new CancellationTokenSource(500); 33 | try 34 | { 35 | await Task.Delay(1000); 36 | var peer = await ipfs.IdAsync(cts.Token); 37 | Assert.Fail("Did not throw TaskCanceledException"); 38 | } 39 | catch (TaskCanceledException) 40 | { 41 | return; 42 | } 43 | ``` 44 | 45 | See also [Task Cancellation](https://docs.microsoft.com/en-us/dotnet/standard/parallel-programming/task-cancellation) 46 | -------------------------------------------------------------------------------- /doc/articles/client.md: -------------------------------------------------------------------------------- 1 | # Accessing IPFS 2 | 3 | IPFS is a distributed peer to peer system. There is no central server! Typically, each machine (peer) runs 4 | a [daemon](daemon.md) that communicates with other peers. 5 | 6 | The [IpfsClient](xref:Ipfs.Http.IpfsClient) provides a simple way for your program to access the daemon 7 | via the [IPFS HTTP API](https://docs.ipfs.io/reference/api/http/) protocol. The client 8 | should be used as a shared object in your program, much like [HttpClient](xref:System.Net.Http.HttpClient). It is 9 | thread safe (re-entrant) and conserves sockets and TCP connections when only one instance is used. 10 | 11 | ```csharp 12 | public class Program 13 | { 14 | static readonly IpfsClient ipfs = new IpfsClient(); 15 | public async Task Main(string[] args) 16 | { 17 | // Get the Peer info of the daemon 18 | var peer = await ipfs.IdAsync(); 19 | } 20 | } 21 | ``` 22 | 23 | ## Core API 24 | 25 | The [Core API](xref:Ipfs.CoreApi.ICoreApi) is a set of interfaces to IPFS features and is implemented by the client. The 26 | [FileSystem](filesystem.md) and [PubSub]() features are most often used. 27 | 28 | ```csharp 29 | const string filename = "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about"; 30 | string text = await ipfs.FileSystem.ReadAllTextAsync(filename); 31 | ``` 32 | 33 | ### Features 34 | 35 | | Feature | Purpose | 36 | | ------- | ------- | 37 | | [Bitswap](xref:Ipfs.CoreApi.IBitswapApi) | Data trading module for IPFS; requests blocks from and sends blocks to other peers | 38 | | [Block](xref:Ipfs.CoreApi.IBlockApi) | Manages the blocks | 39 | | [BlockRepository](xref:Ipfs.CoreApi.IBlockRepositoryApi) | Manages the repository of blocks | 40 | | [Bootstrap](xref:Ipfs.CoreApi.IBootstrapApi) | Trusted peers | 41 | | [Config](xref:Ipfs.CoreApi.IConfigApi) | Manages the configuration of the local peer | 42 | | [Dag](xref:Ipfs.CoreApi.IDagApi) | Manages the IPLD (linked data) Directed Acrylic Graph | 43 | | [Dht](xref:Ipfs.CoreApi.IDhtApi) | Manages the Distributed Hash Table | 44 | | [Dns](xref:Ipfs.CoreApi.IDhtApi) | DNS mapping to IPFS | 45 | | [Misc](xref:Ipfs.CoreApi.IGenericApi) | Some miscellaneous methods | 46 | | [FileSystem](xref:Ipfs.CoreApi.IFileSystemApi) | Manages the files/directories in IPFS | 47 | | [Key](xref:Ipfs.CoreApi.IKeyApi) | Manages the cryptographic keys | 48 | | [Name](xref:Ipfs.CoreApi.INameApi) | Manages the Interplanetary Name Space (IPNS) | 49 | | [Object](xref:Ipfs.CoreApi.IObjectApi) | Manages the IPFS Directed Acrylic Graph | 50 | | [Pin](xref:Ipfs.CoreApi.IPinApi) | Manage objects that are locally stored and permanent | 51 | | [PubSub](xref:Ipfs.CoreApi.IPubSubApi) | Publish and subscribe to topic messages | 52 | | [Swarm](xref:Ipfs.CoreApi.IStatsApi) | Get statistics on IPFS components | 53 | | [Swarm](xref:Ipfs.CoreApi.ISwarmApi) | Manages the swarm of peers | 54 | 55 | -------------------------------------------------------------------------------- /doc/articles/daemon.md: -------------------------------------------------------------------------------- 1 | # IPFS Daemon 2 | 3 | The IPFS daemon is a service that runs on a machine and allows access to other peers on the network. This 4 | is what the [IPFS client](client.md) manages. 5 | 6 | ## Installing 7 | 8 | The authoritive documentmenation is at [https://docs.ipfs.io/introduction/install/](https://docs.ipfs.io/introduction/install/) which 9 | describes the `go-ipfs` implementation. 10 | There is also [js-ipfs](https://docs.ipfs.io/reference/js/overview/) for NodeJS fans. 11 | 12 | ### Windows 13 | 14 | For Windows using [chocolatey](https://chocolatey.org/) 15 | 16 | ``` 17 | > choco install go-ipfs 18 | > ipfs init 19 | > ipfs daemon 20 | ``` 21 | 22 | ## Locating the daemon 23 | 24 | By default the client looks for a deamon at `http://localhost:5001`. This can be overriden by either 25 | setting the environment variable [IpfsHttpUrl](envvars.md) or initialising the client with an URL. 26 | 27 | ```csharp 28 | // js-ipfs likes this address 29 | static readonly IpfsClient ipfs = new IpfsClient("http://127.0.0.1:5002"); 30 | ``` 31 | 32 | 33 | -------------------------------------------------------------------------------- /doc/articles/envvars.md: -------------------------------------------------------------------------------- 1 | # Environment variables 2 | 3 | The following [environment variables](https://msdn.microsoft.com/en-us/library/windows/desktop/ms682653.aspx) 4 | are used to control the behaviour of the library. They override the default value. 5 | 6 | | Name | Description | 7 | | --- | --- | 8 | | IpfsHttpUrl | The [default URL](xref:Ipfs.Http.IpfsClient.DefaultApiUri) to the IPFS HTTP API [daemon](daemon.md). 9 | -------------------------------------------------------------------------------- /doc/articles/filesystem.md: -------------------------------------------------------------------------------- 1 | # IPFS file system 2 | 3 | The official name is [UnixFS](https://docs.ipfs.io/guides/concepts/unixfs/). It allows files and directories of any size 4 | to be added and retrieved from IPFS via the [FileSystem](xref:Ipfs.CoreApi.IFileSystemApi) 5 | and [Object](xref:Ipfs.CoreApi.IObjectApi) API. 6 | 7 | ## Files 8 | 9 | A file has a unique [content id (CID)](xref:Ipfs.Cid) which is the cryptographic hash of the content; see 10 | [CID concept](https://docs.ipfs.io/guides/concepts/cid/) for background information. The file's content is not just the file's 11 | data but is encapsulated with a [protocol buffer](https://en.wikipedia.org/wiki/Protocol_Buffers) encoding of the 12 | [PBNode](https://github.com/ipfs/go-ipfs/blob/0cb22ccf359e05fb5b55a9bf2f9c515bf7d4dba7/merkledag/pb/merkledag.proto#L31-L39) 13 | and [UnixFS Data](https://github.com/ipfs/go-ipfs/blob/0cb22ccf359e05fb5b55a9bf2f9c515bf7d4dba7/unixfs/pb/unixfs.proto#L3-L20). 14 | 15 | Where 16 | - `PBNode.Data` contains unixfs message Data 17 | - unixfs `Data.Data` contans file's data 18 | 19 | When the file's data exceeds the [chunking size](xref:Ipfs.CoreApi.AddFileOptions.ChunkSize), multiple [blocks](xref:Ipfs.CoreApi.IBlockApi) 20 | are generated. The returned CID points to a block that has `PBNode.Links` and no `PBNode.Data`. 21 | 22 | ### Adding a file 23 | 24 | [AddAsync](xref:Ipfs.CoreApi.IFileSystemApi.AddAsync*) is used to add a stream of data to IPFS. It returns a 25 | [FileSystemNode](xref:Ipfs.IFileSystemNode) which 26 | describes the added the data. Of particular import is its [CID](xref:Ipfs.IDataBlock.Id). The helper methods 27 | [AddTextAsync](xref:Ipfs.CoreApi.IFileSystemApi.AddTextAsync*) and [AddFileAsync](xref:Ipfs.CoreApi.IFileSystemApi.AddFileAsync*) are also available. 28 | 29 | All the Add methods accept [options](xref:Ipfs.CoreApi.AddFileOptions) to control how the data is added to IPFS. 30 | 31 | ```csharp 32 | var fsn = await ipfs.FileSystem.AddTextAsync("hello world"); 33 | Console.WriteLine((string)fsn.Id) 34 | 35 | // Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD 36 | ``` 37 | 38 | ### Reading a file 39 | 40 | [ReaFileAsync](xref:Ipfs.CoreApi.IFileSystemApi.ReadFileAsync*) is used to read a stream of data from IPFS. It returns a 41 | [Stream](xref:System.IO.Stream) containing just the file's data NOT the protocol buffer encoded data. 42 | 43 | ```csharp 44 | string path = "Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD"; 45 | using (var stream = await ipfs.FileSystem.ReadFileAsyc(path)) 46 | { 47 | // Do something with the data 48 | } 49 | ``` 50 | 51 | ### Getting a CID 52 | 53 | Normally, you get the CID by [adding](xref:Ipfs.CoreApi.IFileSystemApi.AddAsync*) the file to IPFS. You can avoid adding it 54 | to IPFS by using the [OnlyHash option](xref:Ipfs.CoreApi.AddFileOptions.OnlyHash). 55 | 56 | ```csharp 57 | var options = new AddFileOptions { OnlyHash = true }; 58 | var fsn = await ipfs.FileSystem.AddTextAsync("hello world", options); 59 | Console.WriteLine((string)fsn.Id) 60 | 61 | // Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD 62 | ``` 63 | 64 | -------------------------------------------------------------------------------- /doc/articles/intro.md: -------------------------------------------------------------------------------- 1 | # IPFS HTTP Client 2 | 3 | A .Net client library for the IPFS HTTP API, implemented in C#. It allows you to access the features of [IPFS](https://ipfs.io/). 4 | 5 | The source code is on [GitHub](https://github.com/richardschneider/net-ipfs-http-client) and the 6 | package is published on [NuGet](https://www.nuget.org/packages/ipfs.http.client). 7 | 8 | ![](https://ipfs.io/ipfs/QmQJ68PFMDdAsgCZvA1UVzzn18asVcf7HVvCDgpjiSCAse) 9 | -------------------------------------------------------------------------------- /doc/articles/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Introduction 2 | href: intro.md 3 | - name: Accessing IPFS 4 | href: client.md 5 | - name: File system 6 | href: filesystem.md 7 | - name: Asynchronous I/O 8 | href: async.md 9 | - name: Environment variables 10 | href: envvars.md 11 | - name: IPFS daemon 12 | href: daemon.md 13 | - name: Class Reference 14 | href: ../api/Ipfs.Http.yml 15 | -------------------------------------------------------------------------------- /doc/docfx.json: -------------------------------------------------------------------------------- 1 | { 2 | "metadata": [ 3 | { 4 | "src": [ 5 | { 6 | "files": [ 7 | "src/**.csproj" 8 | ], 9 | "exclude": [ 10 | "**/obj/**", 11 | "**/bin/**", 12 | "_site/**" 13 | ], 14 | "src": ".." 15 | } 16 | ], 17 | "properties": { 18 | "TargetFramework": "net45" 19 | }, 20 | 21 | "dest": "api" 22 | } 23 | ], 24 | "build": { 25 | "content": [ 26 | { 27 | "files": [ 28 | "api/**.yml", 29 | "api/index.md" 30 | ] 31 | }, 32 | { 33 | "files": [ 34 | "articles/**.md", 35 | "articles/**/toc.yml", 36 | "toc.yml", 37 | "*.md" 38 | ], 39 | "exclude": [ 40 | "obj/**", 41 | "_site/**" 42 | ] 43 | } 44 | ], 45 | "resource": [ 46 | { 47 | "files": [ 48 | "images/**" 49 | ], 50 | "exclude": [ 51 | "obj/**", 52 | "_site/**" 53 | ] 54 | } 55 | ], 56 | "overwrite": [ 57 | { 58 | "files": [ 59 | "apidoc/**.md" 60 | ], 61 | "exclude": [ 62 | "obj/**", 63 | "_site/**" 64 | ] 65 | } 66 | ], 67 | "xrefService": [ 68 | "https://xref.docs.microsoft.com/query?uid={uid}" 69 | ], 70 | "xref": [ 71 | "https://richardschneider.github.io/net-ipfs-core/xrefmap.yml" 72 | ], 73 | "globalMetadata": { 74 | "_appTitle": "IPFS HTP Client documentation", 75 | "_appFooter": "Generated by DocFX", 76 | "_appFaviconPath": "images/ipfs-favicon.ico", 77 | "_appLogoPath": "images/ipfs-cs-logo-48x48.png" 78 | }, 79 | "dest": "_site", 80 | "globalMetadataFiles": [], 81 | "fileMetadataFiles": [], 82 | "template": [ 83 | "default" 84 | ], 85 | "postProcessors": [], 86 | "noLangKeyword": false 87 | } 88 | } -------------------------------------------------------------------------------- /doc/images/docs-latest-green.svg: -------------------------------------------------------------------------------- 1 | docsdocslatestlatest -------------------------------------------------------------------------------- /doc/images/ipfs-cs-logo-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/images/ipfs-cs-logo-48x48.png -------------------------------------------------------------------------------- /doc/images/ipfs-cs-logo-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/images/ipfs-cs-logo-64x64.png -------------------------------------------------------------------------------- /doc/images/ipfs-cs-logo.svg: -------------------------------------------------------------------------------- 1 | 2 | 21 | 23 | 24 | 26 | image/svg+xml 27 | 29 | 30 | 31 | 32 | 33 | 53 | 55 | 62 | 66 | 67 | 74 | 78 | 79 | 87 | 91 | 92 | 100 | 104 | 105 | 106 | 110 | 115 | 120 | 121 | 125 | 130 | 135 | 140 | 141 | C # 168 | -------------------------------------------------------------------------------- /doc/images/ipfs-favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/images/ipfs-favicon.ico -------------------------------------------------------------------------------- /doc/images/ipfs-logo.svg: -------------------------------------------------------------------------------- 1 |  2 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | # IPFS HTTP Client 2 | 3 | A .Net client library to access the IPFS, it implements the [IPFS Core API](https://github.com/ipfs/interface-ipfs-core). 4 | The source code is on [GitHub](https://github.com/richardschneider/net-ipfs-http-client) and the 5 | package is published on [NuGet](https://www.nuget.org/packages/Ipfs.Http.Client). 6 | 7 | The interplanetary file system is the permanent web. It is a new hypermedia distribution protocol, addressed by content and identities. IPFS enables the creation of completely distributed applications. It aims to make the web faster, safer, and more open. 8 | 9 | - [Articles](articles/intro.md) on using the API 10 | - [API Documentation](api/Ipfs.Http.yml) describes the core objects in detail 11 | 12 | [![IPFS Core API](https://github.com/ipfs/interface-ipfs-core/raw/master/img/badge.png)](https://github.com/ipfs/interface-ipfs-core) 13 | -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Bold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Bold.eot -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Bold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Bold.ttf -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Bold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Bold.woff -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Bold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Bold.woff2 -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Regular.eot -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Regular.ttf -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Regular.woff -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Regular.woff2 -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Semibold.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Semibold.eot -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Semibold.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Semibold.ttf -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Semibold.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Semibold.woff -------------------------------------------------------------------------------- /doc/template/fonts/Lato-Semibold.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/Lato-Semibold.woff2 -------------------------------------------------------------------------------- /doc/template/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /doc/template/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /doc/template/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /doc/template/fonts/glyphicons-halflings-regular.woff2: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-http-client/6b38ef00fb4d6b75cda232234189f274dcdda67c/doc/template/fonts/glyphicons-halflings-regular.woff2 -------------------------------------------------------------------------------- /doc/template/partials/class.header.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | 3 |

{{>partials/title}}

4 |
{{{summary}}}
5 |
{{{conceptual}}}
6 | 7 | {{#remarks}} 8 |
{{{remarks}}}
9 | {{/remarks}} 10 | 11 |
12 |
{{syntax.content.0.value}}
13 |
14 | 15 | {{#inheritance.0}} 16 |
17 |
{{__global.inheritance}}
18 | {{#inheritance}} 19 |
{{{specName.0.value}}}
20 | {{/inheritance}} 21 |
{{item.name.0.value}}
22 | {{#derivedClasses}} 23 |
{{{specName.0.value}}}
24 | {{/derivedClasses}} 25 |
26 | {{/inheritance.0}} 27 | 28 |
{{__global.namespace}}: {{namespace}}
29 |
{{__global.assembly}}: {{assemblies.0}}.dll
30 | 31 | 32 | {{#syntax.parameters.0}} 33 |
{{__global.parameters}} X
34 | {{/syntax.parameters.0}} 35 | {{#syntax.parameters}} 36 | {{{type.specName.0.value}}} 37 | {{{id}}} 38 | {{{description}}} 39 | {{/syntax.parameters}} 40 | 41 | {{#syntax.return}} 42 |
{{__global.returns}}
43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 |
{{__global.type}}{{__global.description}}
{{{type.specName.0.value}}}{{{description}}}
57 | {{/syntax.return}} 58 | {{#syntax.typeParameters.0}} 59 |
{{__global.typeParameters}}
60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | {{/syntax.typeParameters.0}} 69 | {{#syntax.typeParameters}} 70 | 71 | 72 | 73 | 74 | {{/syntax.typeParameters}} 75 | {{#syntax.typeParameters.0}} 76 | 77 |
{{__global.name}}{{__global.description}}
{{{id}}}{{{description}}}
78 | {{/syntax.typeParameters.0}} 79 | {{#example.0}} 80 |
{{__global.examples}}
81 | {{/example.0}} 82 | {{#example}} 83 | {{{.}}} 84 | {{/example}} -------------------------------------------------------------------------------- /doc/template/partials/class.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | 3 | {{>partials/class.header}} 4 | {{#children}} 5 |

{{>partials/classSubtitle}}

6 | {{#children}} 7 | {{^_disableContribution}} 8 | {{#docurl}} 9 | 10 | | 11 | {{__global.improveThisDoc}} 12 | {{/docurl}} 13 | {{#sourceurl}} 14 | 15 | {{__global.viewSource}} 16 | {{/sourceurl}} 17 | {{/_disableContribution}} 18 | {{#overload}} 19 | 20 | {{/overload}} 21 |

{{name.0.value}}

22 | 23 |
{{{summary}}}
24 |
{{{conceptual}}}
25 |
{{{remarks}}}
26 | 27 | {{#syntax}} 28 |
29 |
{{syntax.content.0.value}}
30 |
31 | 32 | {{#parameters.0}} 33 |
{{__global.parameters}}
34 | {{/parameters.0}} 35 | {{#parameters}} 36 |
37 | {{{type.specName.0.value}}} 38 | {{{id}}} 39 | {{{description}}} 40 |
41 | {{/parameters}} 42 | 43 | {{#return}} 44 |
{{__global.returns}}
45 |
46 | {{{type.specName.0.value}}} 47 | {{{description}}} 48 |
49 | {{/return}} 50 | 51 | {{#typeParameters.0}} 52 |
{{__global.typeParameters}}
53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | {{/typeParameters.0}} 62 | {{#typeParameters}} 63 | 64 | 65 | 66 | 67 | {{/typeParameters}} 68 | {{#typeParameters.0}} 69 | 70 |
{{__global.name}}{{__global.description}}
{{{id}}}{{{description}}}
71 | {{/typeParameters.0}} 72 | {{#fieldValue}} 73 |
{{__global.fieldValue}}
74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 |
{{__global.type}}{{__global.description}}
{{{type.specName.0.value}}}{{{description}}}
88 | {{/fieldValue}} 89 | 90 | {{#propertyValue}} 91 |
{{__global.propertyValue}}
92 |
93 | {{{type.specName.0.value}}} 94 | {{{description}}} 95 |
96 | {{/propertyValue}} 97 | 98 | {{#eventType}} 99 |
{{__global.eventType}}
100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 |
{{__global.type}}{{__global.description}}
{{{type.specName.0.value}}}{{{description}}}
114 | {{/eventType}} 115 | {{/syntax}} 116 | {{#overridden}} 117 |
{{__global.overrides}}
118 |
119 | {{/overridden}} 120 | {{#implements.0}} 121 |
{{__global.implements}}
122 | {{/implements.0}} 123 | {{#implements}} 124 | {{#definition}} 125 |
126 | {{/definition}} 127 | {{^definition}} 128 |
129 | {{/definition}} 130 | {{/implements}} 131 | 132 | {{#example.0}} 133 |
{{__global.examples}}
134 | {{/example.0}} 135 | {{#example}} 136 | {{{.}}} 137 | {{/example}} 138 | 139 | {{#exceptions.0}} 140 |
{{__global.exceptions}}
141 | {{/exceptions.0}} 142 | {{#exceptions}} 143 |
144 | {{{type.specName.0.value}}} 145 | {{{description}}} 146 |
147 | {{/exceptions}} 148 | 149 | {{#seealso.0}} 150 |
{{__global.seealso}}
151 |
152 | {{/seealso.0}} 153 | {{#seealso}} 154 | {{#isCref}} 155 |
{{{type.specName.0.value}}}
156 | {{/isCref}} 157 | {{^isCref}} 158 |
{{{url}}}
159 | {{/isCref}} 160 | {{/seealso}} 161 | {{#seealso.0}} 162 |
163 | {{/seealso.0}} 164 | {{/children}} 165 | {{/children}} 166 | {{#extensionMethods.0}} 167 |

{{__global.extensionMethods}}

168 | {{/extensionMethods.0}} 169 | {{#extensionMethods}} 170 |
171 | {{#definition}} 172 | 173 | {{/definition}} 174 | {{^definition}} 175 | 176 | {{/definition}} 177 |
178 | {{/extensionMethods}} 179 | {{#seealso.0}} 180 |

{{__global.seealso}}

181 |
182 | {{/seealso.0}} 183 | {{#seealso}} 184 | {{#isCref}} 185 |
{{{type.specName.0.value}}}
186 | {{/isCref}} 187 | {{^isCref}} 188 |
{{{url}}}
189 | {{/isCref}} 190 | {{/seealso}} 191 | {{#seealso.0}} 192 |
193 | {{/seealso.0}} 194 | -------------------------------------------------------------------------------- /doc/template/partials/classSubtitle.tmpl.partial: -------------------------------------------------------------------------------- 1 | {{!Copyright (c) Microsoft. All rights reserved. Licensed under the MIT license. See LICENSE file in the project root for full license information.}} 2 | {{#inConstructor}} 3 | {{__global.constructorsInSubtitle}} 4 | {{/inConstructor}} 5 | {{#inField}} 6 | {{__global.fieldsInSubtitle}} 7 | {{/inField}} 8 | {{#inProperty}} 9 | {{__global.propertiesInSubtitle}} 10 | {{/inProperty}} 11 | {{#inMethod}} 12 | {{__global.methodsInSubtitle}} 13 | {{/inMethod}} 14 | {{#inEvent}} 15 | {{__global.eventsInSubtitle}} 16 | {{/inEvent}} 17 | {{#inOperator}} 18 | {{__global.operatorsInSubtitle}} 19 | {{/inOperator}} 20 | {{#inEii}} 21 | {{__global.eiisInSubtitle}} 22 | {{/inEii}} -------------------------------------------------------------------------------- /doc/template/styles/main.css: -------------------------------------------------------------------------------- 1 | html, 2 | body { 3 | font-family: "Lato","proxima-nova","Helvetica Neue",Arial,sans-serif; 4 | height: 100%; 5 | } 6 | 7 | /* http://www.latofonts.com/lato-free-fonts/ */ 8 | @font-face { 9 | font-family: 'Lato'; 10 | src: url(../fonts/Lato-Regular.eot); 11 | src: url(../fonts/Lato-Regular.eot?#iefix) format('embedded-opentype'), 12 | url(../fonts/Lato-Regular.woff2) format('woff2'), 13 | url(../fonts/Lato-Regular.woff) format('woff'), 14 | url(../fonts/Lato-Regular.ttf) format('truetype'); 15 | font-weight: normal; 16 | font-style: normal; 17 | } 18 | @font-face { 19 | font-family: 'Lato'; 20 | src: url(../fonts/Lato-Semibold.eot); 21 | src: url(../fonts/Lato-Semibold.eot?#iefix) format('embedded-opentype'), 22 | url(../fonts/Lato-Semibold.woff2) format('woff2'), 23 | url(../fonts/Lato-Semibold.woff) format('woff'), 24 | url(../fonts/Lato-Semibold.ttf) format('truetype'); 25 | font-weight: 500; 26 | font-style: normal; 27 | } 28 | @font-face { 29 | font-family: 'Lato'; 30 | src: url(../fonts/Lato-Bold.eot); 31 | src: url(../fonts/Lato-Bold.eot?#iefix) format('embedded-opentype'), 32 | url(../fonts/Lato-Bold.woff2) format('woff2'), 33 | url(../fonts/Lato-Bold.woff) format('woff'), 34 | url(../fonts/Lato-Bold.ttf) format('truetype'); 35 | font-weight: bold; 36 | font-style: normal; 37 | } 38 | 39 | p { 40 | line-height: 24px; 41 | font-size: 16px; 42 | } 43 | 44 | .h1,h1{font-size:36px} 45 | .h2,h2{font-size:30px} 46 | .h3,h3{font-size:24px} 47 | .h4,h4{font-size:20px} 48 | .h5,h5{font-size:14px} 49 | .h6,h6{font-size:12px} 50 | 51 | #logo { 52 | padding-top: 5px; 53 | } 54 | -------------------------------------------------------------------------------- /doc/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Articles 2 | href: articles/ 3 | - name: Classes 4 | href: api/ 5 | - name: Github 6 | href: https://github.com/richardschneider/net-ipfs-http-client 7 | -------------------------------------------------------------------------------- /src/Block.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.Serialization; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs.Http 10 | { 11 | /// 12 | [DataContract] 13 | public class Block : IDataBlock 14 | { 15 | long? size; 16 | 17 | /// 18 | [DataMember] 19 | public Cid Id { get; set; } 20 | 21 | /// 22 | [DataMember] 23 | public byte[] DataBytes { get; set; } 24 | 25 | /// 26 | public Stream DataStream 27 | { 28 | get 29 | { 30 | return new MemoryStream(DataBytes, false); 31 | } 32 | } 33 | 34 | /// 35 | [DataMember] 36 | public long Size 37 | { 38 | get 39 | { 40 | if (size.HasValue) 41 | { 42 | return size.Value; 43 | } 44 | return DataBytes.Length; 45 | } 46 | set 47 | { 48 | size = value; 49 | } 50 | } 51 | 52 | } 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/CoreApi/BitswapApi.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using Ipfs.CoreApi; 11 | using System.IO; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class BitswapApi : IBitswapApi 17 | { 18 | IpfsClient ipfs; 19 | 20 | internal BitswapApi(IpfsClient ipfs) 21 | { 22 | this.ipfs = ipfs; 23 | } 24 | 25 | public Task GetAsync(Cid id, CancellationToken cancel = default(CancellationToken)) 26 | { 27 | return ipfs.Block.GetAsync(id, cancel); 28 | } 29 | 30 | public async Task> WantsAsync(MultiHash peer = null, CancellationToken cancel = default(CancellationToken)) 31 | { 32 | var json = await ipfs.DoCommandAsync("bitswap/wantlist", cancel, peer?.ToString()); 33 | var keys = (JArray)(JObject.Parse(json)["Keys"]); 34 | // https://github.com/ipfs/go-ipfs/issues/5077 35 | return keys 36 | .Select(k => 37 | { 38 | if (k.Type == JTokenType.String) 39 | return Cid.Decode(k.ToString()); 40 | var obj = (JObject)k; 41 | return Cid.Decode(obj["/"].ToString()); 42 | }); 43 | } 44 | 45 | public async Task UnwantAsync(Cid id, CancellationToken cancel = default(CancellationToken)) 46 | { 47 | await ipfs.DoCommandAsync("bitswap/unwant", cancel, id); 48 | } 49 | 50 | public async Task LedgerAsync(Peer peer, CancellationToken cancel = default(CancellationToken)) 51 | { 52 | var json = await ipfs.DoCommandAsync("bitswap/ledger", cancel, peer.Id.ToString()); 53 | var o = JObject.Parse(json); 54 | return new BitswapLedger 55 | { 56 | Peer = (string)o["Peer"], 57 | DataReceived = (ulong)o["Sent"], 58 | DataSent = (ulong)o["Recv"], 59 | BlocksExchanged = (ulong)o["Exchanged"] 60 | }; 61 | } 62 | } 63 | 64 | } 65 | -------------------------------------------------------------------------------- /src/CoreApi/BlockApi.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using Ipfs.CoreApi; 11 | using System.IO; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class BlockApi : IBlockApi 17 | { 18 | IpfsClient ipfs; 19 | 20 | internal BlockApi(IpfsClient ipfs) 21 | { 22 | this.ipfs = ipfs; 23 | } 24 | 25 | public async Task GetAsync(Cid id, CancellationToken cancel = default(CancellationToken)) 26 | { 27 | var data = await ipfs.DownloadBytesAsync("block/get", cancel, id); 28 | return new Block 29 | { 30 | DataBytes = data, 31 | Id = id 32 | }; 33 | } 34 | 35 | public async Task PutAsync( 36 | byte[] data, 37 | string contentType = Cid.DefaultContentType, 38 | string multiHash = MultiHash.DefaultAlgorithmName, 39 | string encoding = MultiBase.DefaultAlgorithmName, 40 | bool pin = false, 41 | CancellationToken cancel = default(CancellationToken)) 42 | { 43 | var options = new List(); 44 | if (multiHash != MultiHash.DefaultAlgorithmName || 45 | contentType != Cid.DefaultContentType || 46 | encoding != MultiBase.DefaultAlgorithmName) 47 | { 48 | options.Add($"mhtype={multiHash}"); 49 | options.Add($"format={contentType}"); 50 | options.Add($"cid-base={encoding}"); 51 | } 52 | var json = await ipfs.UploadAsync("block/put", cancel, data, options.ToArray()); 53 | var info = JObject.Parse(json); 54 | Cid cid = (string)info["Key"]; 55 | 56 | if (pin) 57 | { 58 | await ipfs.Pin.AddAsync(cid, recursive: false, cancel: cancel); 59 | } 60 | 61 | return cid; 62 | } 63 | 64 | public async Task PutAsync( 65 | Stream data, 66 | string contentType = Cid.DefaultContentType, 67 | string multiHash = MultiHash.DefaultAlgorithmName, 68 | string encoding = MultiBase.DefaultAlgorithmName, 69 | bool pin = false, 70 | CancellationToken cancel = default(CancellationToken)) 71 | { 72 | var options = new List(); 73 | if (multiHash != MultiHash.DefaultAlgorithmName || 74 | contentType != Cid.DefaultContentType || 75 | encoding != MultiBase.DefaultAlgorithmName) 76 | { 77 | options.Add($"mhtype={multiHash}"); 78 | options.Add($"format={contentType}"); 79 | options.Add($"cid-base={encoding}"); 80 | } 81 | var json = await ipfs.UploadAsync("block/put", cancel, data, null, options.ToArray()); 82 | var info = JObject.Parse(json); 83 | Cid cid = (string)info["Key"]; 84 | 85 | if (pin) 86 | { 87 | await ipfs.Pin.AddAsync(cid, recursive: false, cancel: cancel); 88 | } 89 | 90 | return cid; 91 | } 92 | 93 | public async Task StatAsync(Cid id, CancellationToken cancel = default(CancellationToken)) 94 | { 95 | var json = await ipfs.DoCommandAsync("block/stat", cancel, id); 96 | var info = JObject.Parse(json); 97 | return new Block 98 | { 99 | Size = (long)info["Size"], 100 | Id = (string)info["Key"] 101 | }; 102 | } 103 | 104 | public async Task RemoveAsync(Cid id, bool ignoreNonexistent = false, CancellationToken cancel = default(CancellationToken)) 105 | { 106 | var json = await ipfs.DoCommandAsync("block/rm", cancel, id, "force=" + ignoreNonexistent.ToString().ToLowerInvariant()); 107 | if (json.Length == 0) 108 | return null; 109 | var result = JObject.Parse(json); 110 | var error = (string)result["Error"]; 111 | if (error != null) 112 | throw new HttpRequestException(error); 113 | return (Cid)(string)result["Hash"]; 114 | } 115 | 116 | } 117 | 118 | } 119 | -------------------------------------------------------------------------------- /src/CoreApi/BlockRepositoryApi.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using Ipfs.CoreApi; 11 | using System.IO; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class BlockRepositoryApi : IBlockRepositoryApi 17 | { 18 | IpfsClient ipfs; 19 | 20 | internal BlockRepositoryApi(IpfsClient ipfs) 21 | { 22 | this.ipfs = ipfs; 23 | } 24 | 25 | public async Task RemoveGarbageAsync(CancellationToken cancel = default(CancellationToken)) 26 | { 27 | await ipfs.DoCommandAsync("repo/gc", cancel); 28 | } 29 | 30 | public Task StatisticsAsync(CancellationToken cancel = default(CancellationToken)) 31 | { 32 | return ipfs.DoCommandAsync("repo/stat", cancel); 33 | } 34 | 35 | public async Task VerifyAsync(CancellationToken cancel = default(CancellationToken)) 36 | { 37 | await ipfs.DoCommandAsync("repo/verify", cancel); 38 | } 39 | 40 | public async Task VersionAsync(CancellationToken cancel = default(CancellationToken)) 41 | { 42 | var json = await ipfs.DoCommandAsync("repo/version", cancel); 43 | var info = JObject.Parse(json); 44 | return (string)info["Version"]; 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /src/CoreApi/BootstrapApi.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using Ipfs.CoreApi; 11 | using System.IO; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class BootstrapApi : IBootstrapApi 17 | { 18 | IpfsClient ipfs; 19 | 20 | internal BootstrapApi(IpfsClient ipfs) 21 | { 22 | this.ipfs = ipfs; 23 | } 24 | 25 | public async Task AddAsync(MultiAddress address, CancellationToken cancel = default(CancellationToken)) 26 | { 27 | var json = await ipfs.DoCommandAsync("bootstrap/add", cancel, address.ToString()); 28 | var addrs = (JArray)(JObject.Parse(json)["Peers"]); 29 | var a = addrs.FirstOrDefault(); 30 | if (a == null) 31 | return null; 32 | return new MultiAddress((string)a); 33 | } 34 | 35 | public async Task> AddDefaultsAsync(CancellationToken cancel = default(CancellationToken)) 36 | { 37 | var json = await ipfs.DoCommandAsync("bootstrap/add/default", cancel); 38 | var addrs = (JArray)(JObject.Parse(json)["Peers"]); 39 | return addrs 40 | .Select(a => MultiAddress.TryCreate((string)a)) 41 | .Where(ma => ma != null); 42 | } 43 | 44 | public async Task> ListAsync(CancellationToken cancel = default(CancellationToken)) 45 | { 46 | var json = await ipfs.DoCommandAsync("bootstrap/list", cancel); 47 | var addrs = (JArray)(JObject.Parse(json)["Peers"]); 48 | return addrs 49 | .Select(a => MultiAddress.TryCreate((string)a)) 50 | .Where(ma => ma != null); 51 | } 52 | 53 | public Task RemoveAllAsync(CancellationToken cancel = default(CancellationToken)) 54 | { 55 | return ipfs.DoCommandAsync("bootstrap/rm/all", cancel); 56 | } 57 | 58 | public async Task RemoveAsync(MultiAddress address, CancellationToken cancel = default(CancellationToken)) 59 | { 60 | var json = await ipfs.DoCommandAsync("bootstrap/rm", cancel, address.ToString()); 61 | var addrs = (JArray)(JObject.Parse(json)["Peers"]); 62 | var a = addrs.FirstOrDefault(); 63 | if (a == null) 64 | return null; 65 | return new MultiAddress((string)a); 66 | } 67 | } 68 | 69 | } 70 | -------------------------------------------------------------------------------- /src/CoreApi/ConfigApi.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Ipfs.CoreApi; 10 | 11 | namespace Ipfs.Http 12 | { 13 | 14 | class ConfigApi : IConfigApi 15 | { 16 | IpfsClient ipfs; 17 | 18 | internal ConfigApi(IpfsClient ipfs) 19 | { 20 | this.ipfs = ipfs; 21 | } 22 | 23 | public async Task GetAsync(CancellationToken cancel = default(CancellationToken)) 24 | { 25 | var json = await ipfs.DoCommandAsync("config/show", cancel); 26 | return JObject.Parse(json); 27 | } 28 | 29 | public async Task GetAsync(string key, CancellationToken cancel = default(CancellationToken)) 30 | { 31 | var json = await ipfs.DoCommandAsync("config", cancel, key); 32 | var r = JObject.Parse(json); 33 | return r["Value"]; 34 | } 35 | 36 | public async Task SetAsync(string key, string value, CancellationToken cancel = default(CancellationToken)) 37 | { 38 | var _ = await ipfs.DoCommandAsync("config", cancel, key, "arg=" + value); 39 | return; 40 | } 41 | 42 | public async Task SetAsync(string key, JToken value, CancellationToken cancel = default(CancellationToken)) 43 | { 44 | var _ = await ipfs.DoCommandAsync("config", cancel, 45 | key, 46 | "arg=" + value.ToString(Formatting.None), 47 | "json=true"); 48 | return; 49 | } 50 | 51 | public async Task ReplaceAsync(JObject config) 52 | { 53 | var data = Encoding.UTF8.GetBytes(config.ToString(Formatting.None)); 54 | await ipfs.UploadAsync("config/replace", CancellationToken.None, data); 55 | } 56 | } 57 | 58 | } 59 | -------------------------------------------------------------------------------- /src/CoreApi/DagApi.cs: -------------------------------------------------------------------------------- 1 | using Common.Logging; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Ipfs.CoreApi; 12 | using System.Globalization; 13 | 14 | namespace Ipfs.Http 15 | { 16 | 17 | class DagApi : IDagApi 18 | { 19 | IpfsClient ipfs; 20 | 21 | internal DagApi(IpfsClient ipfs) 22 | { 23 | this.ipfs = ipfs; 24 | } 25 | 26 | 27 | public async Task PutAsync( 28 | JObject data, 29 | string contentType = "dag-cbor", 30 | string multiHash = MultiHash.DefaultAlgorithmName, 31 | string encoding = MultiBase.DefaultAlgorithmName, 32 | bool pin = true, 33 | CancellationToken cancel = default(CancellationToken)) 34 | { 35 | using (var ms = new MemoryStream()) 36 | { 37 | using (var sw = new StreamWriter(ms, new UTF8Encoding(false), 4096, true) { AutoFlush = true }) 38 | using (var jw = new JsonTextWriter(sw)) 39 | { 40 | var serializer = new JsonSerializer 41 | { 42 | Culture = CultureInfo.InvariantCulture 43 | }; 44 | serializer.Serialize(jw, data); 45 | } 46 | ms.Position = 0; 47 | return await PutAsync(ms, contentType, multiHash, encoding, pin, cancel); 48 | } 49 | } 50 | 51 | public async Task PutAsync( 52 | object data, 53 | string contentType = "dag-cbor", 54 | string multiHash = MultiHash.DefaultAlgorithmName, 55 | string encoding = MultiBase.DefaultAlgorithmName, 56 | bool pin = true, 57 | CancellationToken cancel = default(CancellationToken)) 58 | { 59 | using (var ms = new MemoryStream( 60 | Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(data)), 61 | false)) 62 | { 63 | return await PutAsync(ms, contentType, multiHash, encoding, pin, cancel); 64 | } 65 | } 66 | 67 | public async Task PutAsync( 68 | Stream data, 69 | string contentType = "dag-cbor", 70 | string multiHash = MultiHash.DefaultAlgorithmName, 71 | string encoding = MultiBase.DefaultAlgorithmName, 72 | bool pin = true, 73 | CancellationToken cancel = default(CancellationToken)) 74 | { 75 | var json = await ipfs.UploadAsync("dag/put", cancel, 76 | data, null, 77 | $"format={contentType}", 78 | $"pin={pin.ToString().ToLowerInvariant()}", 79 | $"hash={multiHash}", 80 | $"cid-base={encoding}"); 81 | var result = JObject.Parse(json); 82 | return (Cid)(string)result["Cid"]["/"]; 83 | } 84 | 85 | public async Task GetAsync( 86 | Cid id, 87 | CancellationToken cancel = default(CancellationToken)) 88 | { 89 | var json = await ipfs.DoCommandAsync("dag/get", cancel, id); 90 | return JObject.Parse(json); 91 | } 92 | 93 | 94 | public async Task GetAsync( 95 | string path, 96 | CancellationToken cancel = default(CancellationToken)) 97 | { 98 | var json = await ipfs.DoCommandAsync("dag/get", cancel, path); 99 | return JToken.Parse(json); 100 | } 101 | 102 | public async Task GetAsync(Cid id, CancellationToken cancel = default(CancellationToken)) 103 | { 104 | var json = await ipfs.DoCommandAsync("dag/get", cancel, id); 105 | return JsonConvert.DeserializeObject(json); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/CoreApi/DhtApi.cs: -------------------------------------------------------------------------------- 1 | using Common.Logging; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Ipfs.CoreApi; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class DhtApi : IDhtApi 17 | { 18 | static ILog log = LogManager.GetLogger(); 19 | 20 | IpfsClient ipfs; 21 | 22 | internal DhtApi(IpfsClient ipfs) 23 | { 24 | this.ipfs = ipfs; 25 | } 26 | 27 | public Task FindPeerAsync(MultiHash id, CancellationToken cancel = default(CancellationToken)) 28 | { 29 | return ipfs.IdAsync(id, cancel); 30 | } 31 | 32 | public async Task> FindProvidersAsync(Cid id, int limit = 20, Action providerFound = null, CancellationToken cancel = default(CancellationToken)) 33 | { 34 | // TODO: providerFound action 35 | var stream = await ipfs.PostDownloadAsync("dht/findprovs", cancel, id, $"num-providers={limit}"); 36 | return ProviderFromStream(stream, limit); 37 | } 38 | 39 | public Task GetAsync(byte[] key, CancellationToken cancel = default(CancellationToken)) 40 | { 41 | throw new NotImplementedException(); 42 | } 43 | 44 | public Task ProvideAsync(Cid cid, bool advertise = true, CancellationToken cancel = default(CancellationToken)) 45 | { 46 | throw new NotImplementedException(); 47 | } 48 | 49 | public Task PutAsync(byte[] key, out byte[] value, CancellationToken cancel = default(CancellationToken)) 50 | { 51 | throw new NotImplementedException(); 52 | } 53 | 54 | public Task TryGetAsync(byte[] key, out byte[] value, CancellationToken cancel = default(CancellationToken)) 55 | { 56 | throw new NotImplementedException(); 57 | } 58 | 59 | IEnumerable ProviderFromStream(Stream stream, int limit = int.MaxValue) 60 | { 61 | using (var sr = new StreamReader(stream)) 62 | { 63 | var n = 0; 64 | while (!sr.EndOfStream && n < limit) 65 | { 66 | var json = sr.ReadLine(); 67 | if (log.IsDebugEnabled) 68 | log.DebugFormat("Provider {0}", json); 69 | 70 | var r = JObject.Parse(json); 71 | var id = (string)r["ID"]; 72 | if (id != String.Empty) 73 | { 74 | ++n; 75 | yield return new Peer { Id = new MultiHash(id) }; 76 | } 77 | else 78 | { 79 | var responses = (JArray)r["Responses"]; 80 | if (responses != null) 81 | { 82 | foreach (var response in responses) 83 | { 84 | var rid = (string)response["ID"]; 85 | if (rid != String.Empty) 86 | { 87 | ++n; 88 | yield return new Peer { Id = new MultiHash(rid) }; 89 | } 90 | } 91 | } 92 | } 93 | } 94 | } 95 | } 96 | } 97 | 98 | } 99 | -------------------------------------------------------------------------------- /src/CoreApi/DnsApi.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using Ipfs.CoreApi; 11 | using System.IO; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class DnsApi : IDnsApi 17 | { 18 | IpfsClient ipfs; 19 | 20 | internal DnsApi(IpfsClient ipfs) 21 | { 22 | this.ipfs = ipfs; 23 | } 24 | 25 | public async Task ResolveAsync(string name, bool recursive = false, CancellationToken cancel = default(CancellationToken)) 26 | { 27 | var json = await ipfs.DoCommandAsync("dns", cancel, 28 | name, 29 | $"recursive={recursive.ToString().ToLowerInvariant()}"); 30 | var path = (string)(JObject.Parse(json)["Path"]); 31 | return path; 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/CoreApi/FileSystemApi.cs: -------------------------------------------------------------------------------- 1 | using Common.Logging; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Ipfs.CoreApi; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class FileSystemApi : IFileSystemApi 17 | { 18 | static ILog log = LogManager.GetLogger(); 19 | 20 | IpfsClient ipfs; 21 | Lazy emptyFolder; 22 | 23 | internal FileSystemApi(IpfsClient ipfs) 24 | { 25 | this.ipfs = ipfs; 26 | this.emptyFolder = new Lazy(() => ipfs.Object.NewDirectoryAsync().Result); 27 | } 28 | 29 | public async Task AddFileAsync(string path, AddFileOptions options = null, CancellationToken cancel = default(CancellationToken)) 30 | { 31 | using (var stream = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read)) 32 | { 33 | var node = await AddAsync(stream, Path.GetFileName(path), options, cancel); 34 | return node; 35 | } 36 | } 37 | 38 | public Task AddTextAsync(string text, AddFileOptions options = null, CancellationToken cancel = default(CancellationToken)) 39 | { 40 | return AddAsync(new MemoryStream(Encoding.UTF8.GetBytes(text), false), "", options, cancel); 41 | } 42 | 43 | public async Task AddAsync(Stream stream, string name = "", AddFileOptions options = null, CancellationToken cancel = default(CancellationToken)) 44 | { 45 | if (options == null) 46 | options = new AddFileOptions(); 47 | var opts = new List(); 48 | if (!options.Pin) 49 | opts.Add("pin=false"); 50 | if (options.Wrap) 51 | opts.Add("wrap-with-directory=true"); 52 | if (options.RawLeaves) 53 | opts.Add("raw-leaves=true"); 54 | if (options.OnlyHash) 55 | opts.Add("only-hash=true"); 56 | if (options.Trickle) 57 | opts.Add("trickle=true"); 58 | if (options.Progress != null) 59 | opts.Add("progress=true"); 60 | if (options.Hash != MultiHash.DefaultAlgorithmName) 61 | opts.Add($"hash=${options.Hash}"); 62 | if (options.Encoding != MultiBase.DefaultAlgorithmName) 63 | opts.Add($"cid-base=${options.Encoding}"); 64 | if (!string.IsNullOrWhiteSpace(options.ProtectionKey)) 65 | opts.Add($"protect={options.ProtectionKey}"); 66 | opts.Add($"chunker=size-{options.ChunkSize}"); 67 | 68 | var response = await ipfs.Upload2Async("add", cancel, stream, name, opts.ToArray()); 69 | 70 | // The result is a stream of LDJSON objects. 71 | // See https://github.com/ipfs/go-ipfs/issues/4852 72 | FileSystemNode fsn = null; 73 | using (var sr = new StreamReader(response)) 74 | using (var jr = new JsonTextReader(sr) { SupportMultipleContent = true }) 75 | { 76 | while (jr.Read()) 77 | { 78 | var r = await JObject.LoadAsync(jr, cancel); 79 | 80 | // If a progress report. 81 | if (r.ContainsKey("Bytes")) 82 | { 83 | options.Progress?.Report(new TransferProgress 84 | { 85 | Name = (string)r["Name"], 86 | Bytes = (ulong)r["Bytes"] 87 | }); 88 | } 89 | 90 | // Else must be an added file. 91 | else 92 | { 93 | fsn = new FileSystemNode 94 | { 95 | Id = (string)r["Hash"], 96 | Size = long.Parse((string)r["Size"]), 97 | IsDirectory = false, 98 | Name = name, 99 | IpfsClient = ipfs 100 | }; 101 | if (log.IsDebugEnabled) 102 | log.Debug("added " + fsn.Id + " " + fsn.Name); 103 | } 104 | } 105 | } 106 | 107 | fsn.IsDirectory = options.Wrap; 108 | return fsn; 109 | } 110 | 111 | public async Task AddDirectoryAsync(string path, bool recursive = true, AddFileOptions options = null, CancellationToken cancel = default(CancellationToken)) 112 | { 113 | if (options == null) 114 | options = new AddFileOptions(); 115 | options.Wrap = false; 116 | 117 | // Add the files and sub-directories. 118 | path = Path.GetFullPath(path); 119 | var files = Directory 120 | .EnumerateFiles(path) 121 | .Select(p => AddFileAsync(p, options, cancel)); 122 | if (recursive) 123 | { 124 | var folders = Directory 125 | .EnumerateDirectories(path) 126 | .Select(dir => AddDirectoryAsync(dir, recursive, options, cancel)); 127 | files = files.Union(folders); 128 | } 129 | 130 | // go-ipfs v0.4.14 sometimes fails when sending lots of 'add file' 131 | // requests. It's happy with adding one file at a time. 132 | #if true 133 | var links = new List(); 134 | foreach (var file in files) 135 | { 136 | var node = await file; 137 | links.Add(node.ToLink()); 138 | } 139 | #else 140 | var nodes = await Task.WhenAll(files); 141 | var links = nodes.Select(node => node.ToLink()); 142 | #endif 143 | // Create the directory with links to the created files and sub-directories 144 | var folder = emptyFolder.Value.AddLinks(links); 145 | var directory = await ipfs.Object.PutAsync(folder, cancel); 146 | 147 | if (log.IsDebugEnabled) 148 | log.Debug("added " + directory.Id + " " + Path.GetFileName(path)); 149 | return new FileSystemNode 150 | { 151 | Id = directory.Id, 152 | Name = Path.GetFileName(path), 153 | Links = links, 154 | IsDirectory = true, 155 | Size = directory.Size, 156 | IpfsClient = ipfs 157 | }; 158 | 159 | } 160 | 161 | /// 162 | /// Reads the content of an existing IPFS file as text. 163 | /// 164 | /// 165 | /// A path to an existing file, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" 166 | /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" 167 | /// 168 | /// 169 | /// Is used to stop the task. When cancelled, the is raised. 170 | /// 171 | /// 172 | /// The contents of the as a . 173 | /// 174 | public async Task ReadAllTextAsync(string path, CancellationToken cancel = default(CancellationToken)) 175 | { 176 | using (var data = await ReadFileAsync(path, cancel)) 177 | using (var text = new StreamReader(data)) 178 | { 179 | return await text.ReadToEndAsync(); 180 | } 181 | } 182 | 183 | 184 | /// 185 | /// Opens an existing IPFS file for reading. 186 | /// 187 | /// 188 | /// A path to an existing file, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" 189 | /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" 190 | /// 191 | /// 192 | /// Is used to stop the task. When cancelled, the is raised. 193 | /// 194 | /// 195 | /// A to the file contents. 196 | /// 197 | public Task ReadFileAsync(string path, CancellationToken cancel = default(CancellationToken)) 198 | { 199 | return ipfs.DownloadAsync("cat", cancel, path); 200 | } 201 | 202 | public Task ReadFileAsync(string path, long offset, long length = 0, CancellationToken cancel = default(CancellationToken)) 203 | { 204 | // https://github.com/ipfs/go-ipfs/issues/5380 205 | if (offset > int.MaxValue) 206 | throw new NotSupportedException("Only int offsets are currently supported."); 207 | if (length > int.MaxValue) 208 | throw new NotSupportedException("Only int lengths are currently supported."); 209 | 210 | if (length == 0) 211 | length = int.MaxValue; // go-ipfs only accepts int lengths 212 | return ipfs.DownloadAsync("cat", cancel, path, 213 | $"offset={offset}", 214 | $"length={length}"); 215 | } 216 | 217 | /// 218 | /// Get information about the file or directory. 219 | /// 220 | /// 221 | /// A path to an existing file or directory, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" 222 | /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" 223 | /// 224 | /// 225 | /// Is used to stop the task. When cancelled, the is raised. 226 | /// 227 | /// 228 | public async Task ListFileAsync(string path, CancellationToken cancel = default(CancellationToken)) 229 | { 230 | var json = await ipfs.DoCommandAsync("file/ls", cancel, path); 231 | var r = JObject.Parse(json); 232 | var hash = (string)r["Arguments"][path]; 233 | var o = (JObject)r["Objects"][hash]; 234 | var node = new FileSystemNode() 235 | { 236 | Id = (string)o["Hash"], 237 | Size = (long)o["Size"], 238 | IsDirectory = (string)o["Type"] == "Directory", 239 | Links = new FileSystemLink[0], 240 | IpfsClient = ipfs 241 | }; 242 | var links = o["Links"] as JArray; 243 | if (links != null) 244 | { 245 | node.Links = links 246 | .Select(l => new FileSystemLink() 247 | { 248 | Name = (string)l["Name"], 249 | Id = (string)l["Hash"], 250 | Size = (long)l["Size"], 251 | }) 252 | .ToArray(); 253 | } 254 | 255 | return node; 256 | } 257 | 258 | public Task GetAsync(string path, bool compress = false, CancellationToken cancel = default(CancellationToken)) 259 | { 260 | return ipfs.DownloadAsync("get", cancel, path, $"compress={compress}"); 261 | } 262 | } 263 | } 264 | -------------------------------------------------------------------------------- /src/CoreApi/GenericApi.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Globalization; 5 | using System.Linq; 6 | using System.Text; 7 | using System.IO; 8 | using System.Net; 9 | using System.Threading.Tasks; 10 | using System.Threading; 11 | using Ipfs.CoreApi; 12 | using Newtonsoft.Json.Linq; 13 | 14 | namespace Ipfs.Http 15 | { 16 | public partial class IpfsClient : IGenericApi 17 | { 18 | const double TicksPerNanosecond = (double)TimeSpan.TicksPerMillisecond * 0.000001; 19 | 20 | /// 21 | public Task IdAsync(MultiHash peer = null, CancellationToken cancel = default(CancellationToken)) 22 | { 23 | return DoCommandAsync("id", cancel, peer?.ToString()); 24 | } 25 | 26 | /// 27 | public async Task> PingAsync(MultiHash peer, int count = 10, CancellationToken cancel = default(CancellationToken)) 28 | { 29 | var stream = await PostDownloadAsync("ping", cancel, 30 | peer.ToString(), 31 | $"count={count.ToString(CultureInfo.InvariantCulture)}"); 32 | return PingResultFromStream(stream); 33 | } 34 | 35 | /// 36 | public async Task> PingAsync(MultiAddress address, int count = 10, CancellationToken cancel = default(CancellationToken)) 37 | { 38 | var stream = await PostDownloadAsync("ping", cancel, 39 | address.ToString(), 40 | $"count={count.ToString(CultureInfo.InvariantCulture)}"); 41 | return PingResultFromStream(stream); 42 | } 43 | 44 | IEnumerable PingResultFromStream(Stream stream) 45 | { 46 | using (var sr = new StreamReader(stream)) 47 | { 48 | while (!sr.EndOfStream) 49 | { 50 | var json = sr.ReadLine(); 51 | if (log.IsDebugEnabled) 52 | log.DebugFormat("RSP {0}", json); 53 | 54 | var r = JObject.Parse(json); 55 | yield return new PingResult 56 | { 57 | Success = (bool)r["Success"], 58 | Text = (string)r["Text"], 59 | Time = TimeSpan.FromTicks((long)((long)r["Time"] * TicksPerNanosecond)) 60 | }; 61 | } 62 | } 63 | } 64 | 65 | /// 66 | public async Task ResolveAsync(string name, bool recursive = true, CancellationToken cancel = default(CancellationToken)) 67 | { 68 | var json = await DoCommandAsync("resolve", cancel, 69 | name, 70 | $"recursive={recursive.ToString().ToLowerInvariant()}"); 71 | var path = (string)(JObject.Parse(json)["Path"]); 72 | return path; 73 | } 74 | 75 | /// 76 | public async Task ShutdownAsync() 77 | { 78 | await DoCommandAsync("shutdown", default(CancellationToken)); 79 | } 80 | 81 | /// 82 | public Task> VersionAsync(CancellationToken cancel = default(CancellationToken)) 83 | { 84 | return DoCommandAsync>("version", cancel); 85 | } 86 | 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/CoreApi/KeyApi.cs: -------------------------------------------------------------------------------- 1 | using Common.Logging; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Ipfs.CoreApi; 12 | 13 | namespace Ipfs.Http 14 | { 15 | class KeyApi : IKeyApi 16 | { 17 | /// 18 | /// Information about a local key. 19 | /// 20 | public class KeyInfo : IKey 21 | { 22 | /// 23 | public MultiHash Id { get; set; } 24 | 25 | /// 26 | public string Name { get; set; } 27 | 28 | /// 29 | public override string ToString() 30 | { 31 | return Name; 32 | } 33 | 34 | } 35 | IpfsClient ipfs; 36 | 37 | internal KeyApi(IpfsClient ipfs) 38 | { 39 | this.ipfs = ipfs; 40 | } 41 | 42 | public async Task CreateAsync(string name, string keyType, int size, CancellationToken cancel = default(CancellationToken)) 43 | { 44 | return await ipfs.DoCommandAsync("key/gen", cancel, 45 | name, 46 | $"type={keyType}", 47 | $"size={size}"); 48 | } 49 | 50 | public async Task> ListAsync(CancellationToken cancel = default(CancellationToken)) 51 | { 52 | var json = await ipfs.DoCommandAsync("key/list", cancel, null, "l=true"); 53 | var keys = (JArray)(JObject.Parse(json)["Keys"]); 54 | return keys 55 | .Select(k => new KeyInfo 56 | { 57 | Id = (string)k["Id"], 58 | Name = (string)k["Name"] 59 | }); 60 | } 61 | 62 | public async Task RemoveAsync(string name, CancellationToken cancel = default(CancellationToken)) 63 | { 64 | var json = await ipfs.DoCommandAsync("key/rm", cancel, name); 65 | var keys = JObject.Parse(json)["Keys"] as JArray; 66 | 67 | return keys? 68 | .Select(k => new KeyInfo 69 | { 70 | Id = (string)k["Id"], 71 | Name = (string)k["Name"] 72 | }) 73 | .First(); 74 | } 75 | 76 | public async Task RenameAsync(string oldName, string newName, CancellationToken cancel = default(CancellationToken)) 77 | { 78 | var json = await ipfs.DoCommandAsync("key/rename", cancel, oldName, $"arg={newName}"); 79 | var key = JObject.Parse(json); 80 | return new KeyInfo 81 | { 82 | Id = (string)key["Id"], 83 | Name = (string)key["Now"] 84 | }; 85 | } 86 | 87 | public Task ExportAsync(string name, char[] password, CancellationToken cancel = default(CancellationToken)) 88 | { 89 | throw new NotImplementedException(); 90 | } 91 | 92 | public Task ImportAsync(string name, string pem, char[] password = null, CancellationToken cancel = default(CancellationToken)) 93 | { 94 | throw new NotImplementedException(); 95 | } 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /src/CoreApi/NameApi.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Net.Http; 7 | using System.Text; 8 | using System.Threading; 9 | using System.Threading.Tasks; 10 | using Ipfs.CoreApi; 11 | using System.IO; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class NameApi : INameApi 17 | { 18 | IpfsClient ipfs; 19 | 20 | internal NameApi(IpfsClient ipfs) 21 | { 22 | this.ipfs = ipfs; 23 | } 24 | 25 | public async Task PublishAsync(string path, bool resolve = true, string key = "self", TimeSpan? lifetime = null, CancellationToken cancel = default(CancellationToken)) 26 | { 27 | var json = await ipfs.DoCommandAsync("name/publish", cancel, 28 | path, 29 | "lifetime=24h", // TODO 30 | $"resolve={resolve.ToString().ToLowerInvariant()}", 31 | $"key={key}"); 32 | // TODO: lifetime 33 | var info = JObject.Parse(json); 34 | return new NamedContent 35 | { 36 | NamePath = (string)info["Name"], 37 | ContentPath = (string)info["Value"] 38 | }; 39 | } 40 | 41 | public Task PublishAsync(Cid id, string key = "self", TimeSpan? lifetime = null, CancellationToken cancel = default(CancellationToken)) 42 | { 43 | return PublishAsync("/ipfs/" + id.Encode(), false, key, lifetime, cancel); 44 | } 45 | 46 | public async Task ResolveAsync(string name, bool recursive = false, bool nocache = false, CancellationToken cancel = default(CancellationToken)) 47 | { 48 | var json = await ipfs.DoCommandAsync("name/resolve", cancel, 49 | name, 50 | $"recursive={recursive.ToString().ToLowerInvariant()}", 51 | $"nocache={nocache.ToString().ToLowerInvariant()}"); 52 | var path = (string)(JObject.Parse(json)["Path"]); 53 | return path; 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/CoreApi/ObjectApi.cs: -------------------------------------------------------------------------------- 1 | using Common.Logging; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Ipfs.CoreApi; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class ObjectApi : IObjectApi 17 | { 18 | static ILog log = LogManager.GetLogger(); 19 | 20 | IpfsClient ipfs; 21 | 22 | internal ObjectApi(IpfsClient ipfs) 23 | { 24 | this.ipfs = ipfs; 25 | } 26 | 27 | public Task NewDirectoryAsync(CancellationToken cancel = default(CancellationToken)) 28 | { 29 | return NewAsync("unixfs-dir", cancel); 30 | } 31 | 32 | public async Task NewAsync(string template = null, CancellationToken cancel = default(CancellationToken)) 33 | { 34 | var json = await ipfs.DoCommandAsync("object/new", cancel, template); 35 | var hash = (string) (JObject.Parse(json)["Hash"]); 36 | return await GetAsync(hash); 37 | } 38 | 39 | public async Task GetAsync(Cid id, CancellationToken cancel = default(CancellationToken)) 40 | { 41 | var json = await ipfs.DoCommandAsync("object/get", cancel, id); 42 | return GetDagFromJson(json); 43 | } 44 | 45 | public Task PutAsync(byte[] data, IEnumerable links = null, CancellationToken cancel = default(CancellationToken)) 46 | { 47 | return PutAsync(new DagNode(data, links), cancel); 48 | } 49 | 50 | public async Task PutAsync(DagNode node, CancellationToken cancel = default(CancellationToken)) 51 | { 52 | var json = await ipfs.UploadAsync("object/put", cancel, node.ToArray(), "inputenc=protobuf"); 53 | return node; 54 | } 55 | 56 | public Task DataAsync(Cid id, CancellationToken cancel = default(CancellationToken)) 57 | { 58 | return ipfs.DownloadAsync("object/data", cancel, id); 59 | } 60 | 61 | public async Task> LinksAsync(Cid id, CancellationToken cancel = default(CancellationToken)) 62 | { 63 | var json = await ipfs.DoCommandAsync("object/links", cancel, id); 64 | return GetDagFromJson(json).Links; 65 | } 66 | 67 | // TOOD: patch sub API 68 | 69 | DagNode GetDagFromJson(string json) 70 | { 71 | var result = JObject.Parse(json); 72 | byte[] data = null; 73 | var stringData = (string)result["Data"]; 74 | if (stringData != null) 75 | data = Encoding.UTF8.GetBytes(stringData); 76 | var links = ((JArray)result["Links"]) 77 | .Select(link => new DagLink( 78 | (string)link["Name"], 79 | (string)link["Hash"], 80 | (long)link["Size"])); 81 | return new DagNode(data, links); 82 | } 83 | 84 | public async Task StatAsync(Cid id, CancellationToken cancel = default(CancellationToken)) 85 | { 86 | var json = await ipfs.DoCommandAsync("object/stat", cancel, id); 87 | var r = JObject.Parse(json); 88 | 89 | return new ObjectStat 90 | { 91 | LinkCount = (int)r["NumLinks"], 92 | LinkSize = (long)r["LinksSize"], 93 | BlockSize = (long)r["BlockSize"], 94 | DataSize = (long)r["DataSize"], 95 | CumulativeSize = (long)r["CumulativeSize"] 96 | }; 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/CoreApi/PinApi.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | using Ipfs.CoreApi; 10 | 11 | namespace Ipfs.Http 12 | { 13 | 14 | class PinApi : IPinApi 15 | { 16 | IpfsClient ipfs; 17 | 18 | internal PinApi(IpfsClient ipfs) 19 | { 20 | this.ipfs = ipfs; 21 | } 22 | 23 | public async Task> AddAsync(string path, bool recursive = true, CancellationToken cancel = default(CancellationToken)) 24 | { 25 | var opts = "recursive=" + recursive.ToString().ToLowerInvariant(); 26 | var json = await ipfs.DoCommandAsync("pin/add", cancel, path, opts); 27 | return ((JArray)JObject.Parse(json)["Pins"]) 28 | .Select(p => (Cid)(string)p); 29 | } 30 | 31 | public async Task> ListAsync(CancellationToken cancel = default(CancellationToken)) 32 | { 33 | var json = await ipfs.DoCommandAsync("pin/ls", cancel); 34 | var keys = (JObject)(JObject.Parse(json)["Keys"]); 35 | return keys 36 | .Properties() 37 | .Select(p => (Cid)p.Name); 38 | } 39 | 40 | public async Task> RemoveAsync(Cid id, bool recursive = true, CancellationToken cancel = default(CancellationToken)) 41 | { 42 | var opts = "recursive=" + recursive.ToString().ToLowerInvariant(); 43 | var json = await ipfs.DoCommandAsync("pin/rm", cancel, id, opts); 44 | return ((JArray)JObject.Parse(json)["Pins"]) 45 | .Select(p => (Cid)(string)p); 46 | } 47 | 48 | } 49 | 50 | } 51 | -------------------------------------------------------------------------------- /src/CoreApi/PubSubApi.cs: -------------------------------------------------------------------------------- 1 | using Common.Logging; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Ipfs.CoreApi; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class PubSubApi : IPubSubApi 17 | { 18 | static ILog log = LogManager.GetLogger(); 19 | 20 | IpfsClient ipfs; 21 | 22 | internal PubSubApi(IpfsClient ipfs) 23 | { 24 | this.ipfs = ipfs; 25 | } 26 | 27 | public async Task> SubscribedTopicsAsync(CancellationToken cancel = default(CancellationToken)) 28 | { 29 | var json = await ipfs.DoCommandAsync("pubsub/ls", cancel); 30 | var result = JObject.Parse(json); 31 | var strings = result["Strings"] as JArray; 32 | if (strings == null) return new string[0]; 33 | return strings.Select(s => (string)s); 34 | } 35 | 36 | public async Task> PeersAsync(string topic = null, CancellationToken cancel = default(CancellationToken)) 37 | { 38 | var json = await ipfs.DoCommandAsync("pubsub/peers", cancel, topic); 39 | var result = JObject.Parse(json); 40 | var strings = result["Strings"] as JArray; 41 | if (strings == null) return new Peer[0]; 42 | return strings.Select(s => new Peer { Id = (string)s } ); 43 | } 44 | 45 | public Task PublishAsync(string topic, byte[] message, CancellationToken cancel = default(CancellationToken)) 46 | { 47 | var url = new StringBuilder(); 48 | url.Append("/api/v0/pubsub/pub"); 49 | url.Append("?arg="); 50 | url.Append(System.Net.WebUtility.UrlEncode(topic)); 51 | url.Append("&arg="); 52 | var data = Encoding.ASCII.GetString(System.Net.WebUtility.UrlEncodeToBytes(message, 0, message.Length)); 53 | url.Append(data); 54 | return ipfs.DoCommandAsync(new Uri(ipfs.ApiUri, url.ToString()), cancel); 55 | } 56 | 57 | public Task PublishAsync(string topic, Stream message, CancellationToken cancel = default(CancellationToken)) 58 | { 59 | using (MemoryStream ms = new MemoryStream()) 60 | { 61 | message.CopyTo(ms); 62 | return PublishAsync(topic, ms.ToArray(), cancel); 63 | } 64 | } 65 | 66 | public async Task PublishAsync(string topic, string message, CancellationToken cancel = default(CancellationToken)) 67 | { 68 | var _ = await ipfs.DoCommandAsync("pubsub/pub", cancel, topic, "arg=" + message); 69 | return; 70 | } 71 | 72 | public async Task SubscribeAsync(string topic, Action handler, CancellationToken cancellationToken) 73 | { 74 | var messageStream = await ipfs.PostDownloadAsync("pubsub/sub", cancellationToken, topic); 75 | var sr = new StreamReader(messageStream); 76 | 77 | #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed 78 | Task.Run(() => ProcessMessages(topic, handler, sr, cancellationToken)); 79 | #pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed 80 | 81 | return; 82 | } 83 | 84 | void ProcessMessages(string topic, Action handler, StreamReader sr, CancellationToken ct) 85 | { 86 | log.DebugFormat("Start listening for '{0}' messages", topic); 87 | 88 | // .Net needs a ReadLine(CancellationToken) 89 | // As a work-around, we register a function to close the stream 90 | ct.Register(() => sr.Dispose()); 91 | try 92 | { 93 | while (!sr.EndOfStream && !ct.IsCancellationRequested) 94 | { 95 | var json = sr.ReadLine(); 96 | if (json == null) 97 | break; 98 | if (log.IsDebugEnabled) 99 | log.DebugFormat("PubSub message {0}", json); 100 | 101 | // go-ipfs 0.4.13 and earlier always send empty JSON 102 | // as the first response. 103 | if (json == "{}") 104 | continue; 105 | 106 | if (!ct.IsCancellationRequested) 107 | { 108 | handler(new PublishedMessage(json)); 109 | } 110 | } 111 | } 112 | catch (Exception e) 113 | { 114 | // Do not report errors when cancelled. 115 | if (!ct.IsCancellationRequested) 116 | log.Error(e); 117 | } 118 | finally 119 | { 120 | sr.Dispose(); 121 | } 122 | 123 | log.DebugFormat("Stop listening for '{0}' messages", topic); 124 | } 125 | 126 | } 127 | 128 | } 129 | -------------------------------------------------------------------------------- /src/CoreApi/StatsApi.cs: -------------------------------------------------------------------------------- 1 | using Common.Logging; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Ipfs.CoreApi; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class StatApi : IStatsApi 17 | { 18 | IpfsClient ipfs; 19 | 20 | internal StatApi(IpfsClient ipfs) 21 | { 22 | this.ipfs = ipfs; 23 | } 24 | 25 | public Task BandwidthAsync(CancellationToken cancel = default(CancellationToken)) 26 | { 27 | return ipfs.DoCommandAsync("stats/bw", cancel); 28 | } 29 | 30 | public async Task BitswapAsync(CancellationToken cancel = default(CancellationToken)) 31 | { 32 | var json = await ipfs.DoCommandAsync("stats/bitswap", cancel); 33 | var stat = JObject.Parse(json); 34 | return new BitswapData 35 | { 36 | BlocksReceived = (ulong)stat["BlocksReceived"], 37 | DataReceived = (ulong)stat["DataReceived"], 38 | BlocksSent = (ulong)stat["BlocksSent"], 39 | DataSent = (ulong)stat["DataSent"], 40 | DupBlksReceived = (ulong)stat["DupBlksReceived"], 41 | DupDataReceived = (ulong)stat["DupDataReceived"], 42 | ProvideBufLen = (int)stat["ProvideBufLen"], 43 | Peers = ((JArray)stat["Peers"]).Select(s => new MultiHash((string)s)), 44 | Wantlist = ((JArray)stat["Wantlist"]).Select(o => Cid.Decode(o["/"].ToString())) 45 | }; 46 | } 47 | 48 | public Task RepositoryAsync(CancellationToken cancel = default(CancellationToken)) 49 | { 50 | return ipfs.DoCommandAsync("stats/repo", cancel); 51 | } 52 | 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/CoreApi/SwarmApi.cs: -------------------------------------------------------------------------------- 1 | using Common.Logging; 2 | using Newtonsoft.Json; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | using Ipfs.CoreApi; 12 | 13 | namespace Ipfs.Http 14 | { 15 | 16 | class SwarmApi : ISwarmApi 17 | { 18 | IpfsClient ipfs; 19 | 20 | internal SwarmApi(IpfsClient ipfs) 21 | { 22 | this.ipfs = ipfs; 23 | } 24 | 25 | public async Task> AddressesAsync(CancellationToken cancel = default(CancellationToken)) 26 | { 27 | var json = await ipfs.DoCommandAsync("swarm/addrs", cancel); 28 | return ((JObject)JObject.Parse(json)["Addrs"]) 29 | .Properties() 30 | .Select(p => new Peer { 31 | Id = p.Name, 32 | Addresses = ((JArray)p.Value) 33 | .Select(a => MultiAddress.TryCreate((string)a)) 34 | .Where(ma => ma != null) 35 | }); 36 | } 37 | 38 | public async Task> PeersAsync(CancellationToken cancel = default(CancellationToken)) 39 | { 40 | var json = await ipfs.DoCommandAsync("swarm/peers", cancel, null, "verbose=true"); 41 | var result = JObject.Parse(json); 42 | 43 | // Older servers return an array of strings 44 | var strings = (JArray)result["Strings"]; 45 | if (strings != null) 46 | { 47 | return strings 48 | .Select(s => 49 | { 50 | var parts = ((string)s).Split(' '); 51 | var address = new MultiAddress(parts[0]); 52 | return new Peer 53 | { 54 | Id = address.PeerId, 55 | ConnectedAddress = parts[0], 56 | Latency = Duration.Parse(parts[1]) 57 | }; 58 | }); 59 | } 60 | 61 | // Current servers return JSON 62 | var peers = (JArray)result["Peers"]; 63 | if (peers != null) 64 | { 65 | return peers.Select(p => new Peer 66 | { 67 | Id = (string)p["Peer"], 68 | ConnectedAddress = new MultiAddress((string)p["Addr"] + "/ipfs/" + (string)p["Peer"]), 69 | Latency = Duration.Parse((string)p["Latency"]) 70 | }); 71 | } 72 | 73 | // Hmmm. Another change we can handle 74 | throw new FormatException("Unknown response from 'swarm/peers"); 75 | } 76 | 77 | public async Task ConnectAsync(MultiAddress address, CancellationToken cancel = default(CancellationToken)) 78 | { 79 | await ipfs.DoCommandAsync("swarm/connect", cancel, address.ToString()); 80 | } 81 | 82 | public async Task DisconnectAsync(MultiAddress address, CancellationToken cancel = default(CancellationToken)) 83 | { 84 | await ipfs.DoCommandAsync("swarm/disconnect", cancel, address.ToString()); 85 | } 86 | 87 | public async Task AddAddressFilterAsync(MultiAddress address, bool persist = false, CancellationToken cancel = default(CancellationToken)) 88 | { 89 | // go-ipfs always does persist, https://github.com/ipfs/go-ipfs/issues/4605 90 | var json = await ipfs.DoCommandAsync("swarm/filters/add", cancel, address.ToString()); 91 | var addrs = (JArray)(JObject.Parse(json)["Strings"]); 92 | var a = addrs.FirstOrDefault(); 93 | if (a == null) 94 | return null; 95 | return new MultiAddress((string)a); 96 | } 97 | 98 | public async Task> ListAddressFiltersAsync(bool persist = false, CancellationToken cancel = default(CancellationToken)) 99 | { 100 | JArray addrs; 101 | if (persist) 102 | { 103 | addrs = await ipfs.Config.GetAsync("Swarm.AddrFilters", cancel) as JArray; 104 | } 105 | else 106 | { 107 | var json = await ipfs.DoCommandAsync("swarm/filters", cancel); 108 | addrs = (JObject.Parse(json)["Strings"]) as JArray; 109 | } 110 | 111 | if (addrs == null) 112 | return new MultiAddress[0]; 113 | return addrs 114 | .Select(a => MultiAddress.TryCreate((string)a)) 115 | .Where(ma => ma != null); 116 | } 117 | 118 | public async Task RemoveAddressFilterAsync(MultiAddress address, bool persist = false, CancellationToken cancel = default(CancellationToken)) 119 | { 120 | // go-ipfs always does persist, https://github.com/ipfs/go-ipfs/issues/4605 121 | var json = await ipfs.DoCommandAsync("swarm/filters/rm", cancel, address.ToString()); 122 | var addrs = (JArray)(JObject.Parse(json)["Strings"]); 123 | var a = addrs.FirstOrDefault(); 124 | if (a == null) 125 | return null; 126 | return new MultiAddress((string)a); 127 | } 128 | } 129 | 130 | } 131 | -------------------------------------------------------------------------------- /src/FileSystemLink.cs: -------------------------------------------------------------------------------- 1 |  2 | namespace Ipfs.Http 3 | { 4 | /// 5 | /// A link to another file system node in IPFS. 6 | /// 7 | public class FileSystemLink : IFileSystemLink 8 | { 9 | /// 10 | public string Name { get; set; } 11 | 12 | /// 13 | public Cid Id { get; set; } 14 | 15 | /// 16 | public long Size { get; set; } 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /src/FileSystemNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Runtime.Serialization; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs.Http 10 | { 11 | /// 12 | [DataContract] 13 | public class FileSystemNode : IFileSystemNode 14 | { 15 | IpfsClient ipfsClient; 16 | IEnumerable links; 17 | long? size; 18 | bool? isDirectory; 19 | 20 | /// 21 | public byte[] DataBytes 22 | { 23 | get 24 | { 25 | using (var stream = DataStream) 26 | { 27 | if (DataStream == null) 28 | return null; 29 | 30 | using (var data = new MemoryStream()) 31 | { 32 | stream.CopyTo(data); 33 | return data.ToArray(); 34 | } 35 | } 36 | } 37 | } 38 | 39 | /// 40 | public Stream DataStream 41 | { 42 | get 43 | { 44 | return IpfsClient?.FileSystem.ReadFileAsync(Id).Result; 45 | } 46 | } 47 | 48 | /// 49 | [DataMember] 50 | public Cid Id { get; set; } 51 | 52 | /// 53 | [DataMember] 54 | public IEnumerable Links { 55 | get 56 | { 57 | if (links == null) GetInfo(); 58 | return links; 59 | } 60 | set 61 | { 62 | links = value; 63 | } 64 | } 65 | 66 | /// 67 | /// Size of the file contents. 68 | /// 69 | /// 70 | /// This is the size of the file not the raw encoded contents 71 | /// of the block. 72 | /// 73 | [DataMember] 74 | public long Size 75 | { 76 | get 77 | { 78 | if (!size.HasValue) GetInfo(); 79 | return size.Value; 80 | } 81 | set 82 | { 83 | size = value; 84 | } 85 | } 86 | 87 | /// 88 | /// Determines if the link is a directory (folder). 89 | /// 90 | /// 91 | /// true if the link is a directory; Otherwise false, 92 | /// the link is some type of a file. 93 | /// 94 | [DataMember] 95 | public bool IsDirectory 96 | { 97 | get 98 | { 99 | if (!isDirectory.HasValue) GetInfo(); 100 | return isDirectory.Value; 101 | } 102 | set 103 | { 104 | isDirectory = value; 105 | } 106 | } 107 | 108 | /// 109 | /// The file name of the IPFS node. 110 | /// 111 | [DataMember] 112 | public string Name { get; set; } 113 | 114 | /// 115 | public IFileSystemLink ToLink(string name = "") 116 | { 117 | var link = new FileSystemLink 118 | { 119 | Name = string.IsNullOrWhiteSpace(name) ? Name : name, 120 | Id = Id, 121 | Size = Size, 122 | }; 123 | return link; 124 | } 125 | 126 | /// 127 | /// The client to IPFS. 128 | /// 129 | /// 130 | /// Used to fetch additional information on the node. 131 | /// 132 | public IpfsClient IpfsClient 133 | { 134 | get 135 | { 136 | if (ipfsClient == null) 137 | { 138 | lock (this) 139 | { 140 | ipfsClient = new IpfsClient(); 141 | } 142 | } 143 | return ipfsClient; 144 | } 145 | set 146 | { 147 | ipfsClient = value; 148 | } 149 | } 150 | 151 | void GetInfo() 152 | { 153 | var node = IpfsClient.FileSystem.ListFileAsync(Id).Result; 154 | this.IsDirectory = node.IsDirectory; 155 | this.Links = node.Links; 156 | this.Size = node.Size; 157 | } 158 | 159 | } 160 | } 161 | -------------------------------------------------------------------------------- /src/IpfsHttpClient.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | netstandard14;netstandard2;net45 5 | Ipfs.Http.Client 6 | Ipfs.Http 7 | bin\$(Configuration)\$(TargetFramework)\$(AssemblyName).xml 8 | full 9 | true 10 | 11 | 12 | 0.42 13 | 0.42 14 | 15 | 16 | Ipfs.Http.Client 17 | Richard Schneider 18 | IPFS HTTP Client 19 | Provides .Net client access to the InterPlanetary File System. 20 | false 21 | https://github.com/richardschneider/net-ipfs-http-client/releases 22 | © 2015-2019 Richard Schneider 23 | ipfs peer-to-peer distributed file-system 24 | True 25 | https://github.com/richardschneider/net-ipfs-http-client 26 | https://raw.githubusercontent.com/richardschneider/net-ipfs-core/master/doc/images/ipfs-cs-logo-64x64.png 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | -------------------------------------------------------------------------------- /src/MerkleNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using Newtonsoft.Json.Linq; 6 | using System.IO; 7 | using System.Runtime.Serialization; 8 | 9 | namespace Ipfs.Http 10 | { 11 | /// 12 | /// The IPFS MerkleDag is the datastructure at the heart of IPFS. It is an acyclic directed graph whose edges are hashes. 13 | /// 14 | /// 15 | /// Initially an MerkleNode is just constructed with its Cid. 16 | /// 17 | [DataContract] 18 | public class MerkleNode : IMerkleNode, IEquatable 19 | { 20 | bool hasBlockStats; 21 | long blockSize; 22 | string name; 23 | IEnumerable links; 24 | IpfsClient ipfsClient; 25 | 26 | /// 27 | /// Creates a new instance of the with the specified 28 | /// and optional . 29 | /// 30 | /// 31 | /// The of the node. 32 | /// 33 | /// A name for the node. 34 | public MerkleNode(Cid id, string name = null) 35 | { 36 | if (id == null) 37 | throw new ArgumentNullException("id"); 38 | 39 | Id = id; 40 | Name = name; 41 | } 42 | 43 | /// 44 | /// Creates a new instance of the with the specified 45 | /// cid and optional . 46 | /// 47 | /// 48 | /// The string representation of a of the node or "/ipfs/cid". 49 | /// 50 | /// A name for the node. 51 | public MerkleNode(string path, string name = null) 52 | { 53 | if (string.IsNullOrWhiteSpace(path)) 54 | throw new ArgumentNullException("path"); 55 | 56 | if (path.StartsWith("/ipfs/")) 57 | path = path.Substring(6); 58 | 59 | Id = Cid.Decode(path); 60 | Name = name; 61 | } 62 | 63 | /// 64 | /// Creates a new instance of the from the 65 | /// . 66 | /// 67 | /// The link to a node. 68 | public MerkleNode(IMerkleLink link) 69 | { 70 | Id = link.Id; 71 | Name = link.Name; 72 | blockSize = link.Size; 73 | hasBlockStats = true; 74 | } 75 | 76 | internal IpfsClient IpfsClient 77 | { 78 | get 79 | { 80 | if (ipfsClient == null) 81 | { 82 | lock (this) 83 | { 84 | ipfsClient = new IpfsClient(); 85 | } 86 | } 87 | return ipfsClient; 88 | } 89 | set 90 | { 91 | ipfsClient = value; 92 | } 93 | } 94 | 95 | /// 96 | [DataMember] 97 | public Cid Id { get; private set; } 98 | 99 | /// 100 | /// The name for the node. If unknown it is "" (not null). 101 | /// 102 | [DataMember] 103 | public string Name 104 | { 105 | get { return name; } 106 | set { name = value ?? string.Empty; } 107 | } 108 | 109 | /// 110 | /// Size of the raw, encoded node. 111 | /// 112 | [DataMember] 113 | public long BlockSize 114 | { 115 | get 116 | { 117 | GetBlockStats(); 118 | return blockSize; 119 | } 120 | } 121 | 122 | /// 123 | /// 124 | [DataMember] 125 | public long Size 126 | { 127 | get 128 | { 129 | return BlockSize; 130 | } 131 | } 132 | 133 | 134 | /// 135 | [DataMember] 136 | public IEnumerable Links 137 | { 138 | get 139 | { 140 | if (links == null) 141 | { 142 | links = IpfsClient.Object.LinksAsync(Id).Result; 143 | } 144 | 145 | return links; 146 | } 147 | } 148 | 149 | /// 150 | [DataMember] 151 | public byte[] DataBytes 152 | { 153 | get 154 | { 155 | return IpfsClient.Block.GetAsync(Id).Result.DataBytes; 156 | } 157 | } 158 | 159 | /// 160 | public Stream DataStream 161 | { 162 | get 163 | { 164 | return IpfsClient.Block.GetAsync(Id).Result.DataStream; 165 | } 166 | } 167 | 168 | /// 169 | public IMerkleLink ToLink(string name = null) 170 | { 171 | return new DagLink(name ?? Name, Id, BlockSize); 172 | } 173 | 174 | /// 175 | /// Get block statistics about the node, ipfs block stat key 176 | /// 177 | /// 178 | /// The object stats include the block stats. 179 | /// 180 | void GetBlockStats() 181 | { 182 | if (hasBlockStats) 183 | return; 184 | 185 | var stats = IpfsClient.Block.StatAsync(Id).Result; 186 | blockSize = stats.Size; 187 | 188 | hasBlockStats = true; 189 | } 190 | 191 | /// 192 | public override int GetHashCode() 193 | { 194 | return Id.GetHashCode(); 195 | } 196 | 197 | /// 198 | public override bool Equals(object obj) 199 | { 200 | var that = obj as MerkleNode; 201 | return that != null && this.Id == that.Id; 202 | } 203 | 204 | /// 205 | public bool Equals(MerkleNode that) 206 | { 207 | return that != null && this.Id == that.Id; 208 | } 209 | 210 | /// 211 | /// TODO 212 | /// 213 | public static bool operator ==(MerkleNode a, MerkleNode b) 214 | { 215 | if (object.ReferenceEquals(a, b)) return true; 216 | if (object.ReferenceEquals(a, null)) return false; 217 | if (object.ReferenceEquals(b, null)) return false; 218 | 219 | return a.Equals(b); 220 | } 221 | 222 | /// 223 | /// TODO 224 | /// 225 | public static bool operator !=(MerkleNode a, MerkleNode b) 226 | { 227 | if (object.ReferenceEquals(a, b)) return false; 228 | if (object.ReferenceEquals(a, null)) return true; 229 | if (object.ReferenceEquals(b, null)) return true; 230 | 231 | return !a.Equals(b); 232 | } 233 | 234 | /// 235 | public override string ToString() 236 | { 237 | return "/ipfs/" + Id; 238 | } 239 | 240 | /// 241 | /// TODO 242 | /// 243 | static public implicit operator MerkleNode(string hash) 244 | { 245 | return new MerkleNode(hash); 246 | } 247 | 248 | } 249 | } 250 | -------------------------------------------------------------------------------- /src/PublishedMessage.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.IO; 6 | using System.Text; 7 | using System.Runtime.Serialization; 8 | 9 | namespace Ipfs.Http 10 | { 11 | /// 12 | /// A published message. 13 | /// 14 | /// 15 | /// The is used to publish and subsribe to a message. 16 | /// 17 | [DataContract] 18 | public class PublishedMessage : IPublishedMessage 19 | { 20 | /// 21 | /// Creates a new instance of from the 22 | /// specified JSON string. 23 | /// 24 | /// 25 | /// The JSON representation of a published message. 26 | /// 27 | public PublishedMessage(string json) 28 | { 29 | var o = JObject.Parse(json); 30 | this.Sender = Convert.FromBase64String((string)o["from"]).ToBase58(); 31 | this.SequenceNumber = Convert.FromBase64String((string)o["seqno"]); 32 | this.DataBytes = Convert.FromBase64String((string)o["data"]); 33 | var topics = (JArray) (o["topicIDs"]); 34 | this.Topics = topics.Select(t => (string)t); 35 | } 36 | 37 | /// 38 | [DataMember] 39 | public Peer Sender { get; private set; } 40 | 41 | /// 42 | [DataMember] 43 | public IEnumerable Topics { get; private set; } 44 | 45 | /// 46 | [DataMember] 47 | public byte[] SequenceNumber { get; private set; } 48 | 49 | /// 50 | [DataMember] 51 | public byte[] DataBytes { get; private set; } 52 | 53 | /// 54 | public Stream DataStream 55 | { 56 | get 57 | { 58 | return new MemoryStream(DataBytes, false); 59 | } 60 | } 61 | 62 | /// 63 | [DataMember] 64 | public long Size 65 | { 66 | get { return DataBytes.Length; } 67 | } 68 | /// 69 | /// Contents as a string. 70 | /// 71 | /// 72 | /// The contents interpreted as a UTF-8 string. 73 | /// 74 | public string DataString 75 | { 76 | get 77 | { 78 | return Encoding.UTF8.GetString(DataBytes); 79 | } 80 | } 81 | 82 | /// > 83 | /// NOT SUPPORTED. 84 | /// 85 | /// 86 | /// A published message does not have a content id. 87 | /// 88 | public Cid Id => throw new NotSupportedException(); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/TrustedPeerCollection.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json; 2 | using System; 3 | using System.Collections; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.IO; 8 | using System.Net; 9 | using System.Threading; 10 | 11 | namespace Ipfs.Http 12 | { 13 | /// 14 | /// A list of trusted peers. 15 | /// 16 | /// 17 | /// This is the list of peers that are initially trusted by IPFS. Its equivalent to the 18 | /// ipfs bootstrap command. 19 | /// 20 | /// 21 | /// A series of . Each address ends with an IPNS node id, for 22 | /// example "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ". 23 | /// 24 | public class TrustedPeerCollection : ICollection 25 | { 26 | class BootstrapListResponse 27 | { 28 | public MultiAddress[] Peers { get; set; } 29 | } 30 | 31 | IpfsClient ipfs; 32 | MultiAddress[] peers; 33 | 34 | internal TrustedPeerCollection(IpfsClient ipfs) 35 | { 36 | this.ipfs = ipfs; 37 | } 38 | 39 | /// 40 | public void Add(MultiAddress peer) 41 | { 42 | if (peer == null) 43 | throw new ArgumentNullException(); 44 | 45 | ipfs.DoCommandAsync("bootstrap/add", default(CancellationToken), peer.ToString()).Wait(); 46 | peers = null; 47 | } 48 | 49 | /// 50 | /// Add the default bootstrap nodes to the trusted peers. 51 | /// 52 | /// 53 | /// Equivalent to ipfs bootstrap add --default. 54 | /// 55 | public void AddDefaultNodes() 56 | { 57 | ipfs.DoCommandAsync("bootstrap/add", default(CancellationToken), null, "default=true").Wait(); 58 | peers = null; 59 | } 60 | 61 | /// 62 | /// Remove all the trusted peers. 63 | /// 64 | /// 65 | /// Equivalent to ipfs bootstrap rm --all. 66 | /// 67 | public void Clear() 68 | { 69 | ipfs.DoCommandAsync("bootstrap/rm", default(CancellationToken), null, "all=true").Wait(); 70 | peers = null; 71 | } 72 | 73 | /// 74 | public bool Contains(MultiAddress item) 75 | { 76 | Fetch(); 77 | return peers.Contains(item); 78 | } 79 | 80 | /// 81 | public void CopyTo(MultiAddress[] array, int index) 82 | { 83 | Fetch(); 84 | peers.CopyTo(array, index); 85 | } 86 | 87 | /// 88 | public int Count 89 | { 90 | get 91 | { 92 | if (peers == null) 93 | Fetch(); 94 | return peers.Count(); 95 | } 96 | } 97 | 98 | /// 99 | public bool IsReadOnly 100 | { 101 | get { return false; } 102 | } 103 | 104 | /// 105 | /// Remove the trusted peer. 106 | /// 107 | /// 108 | /// Equivalent to ipfs bootstrap rm peer. 109 | /// 110 | public bool Remove(MultiAddress peer) 111 | { 112 | if (peer == null) 113 | throw new ArgumentNullException(); 114 | 115 | ipfs.DoCommandAsync("bootstrap/rm", default(CancellationToken), peer.ToString()).Wait(); 116 | peers = null; 117 | return true; 118 | } 119 | 120 | /// 121 | public IEnumerator GetEnumerator() 122 | { 123 | Fetch(); 124 | return ((IEnumerable) peers).GetEnumerator(); 125 | } 126 | 127 | /// 128 | IEnumerator IEnumerable.GetEnumerator() 129 | { 130 | Fetch(); 131 | return peers.GetEnumerator(); 132 | } 133 | 134 | void Fetch() 135 | { 136 | peers = ipfs.DoCommandAsync("bootstrap/list", default(CancellationToken)).Result.Peers; 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /test/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /test/BlockTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.Linq; 5 | 6 | namespace Ipfs.Http 7 | { 8 | 9 | [TestClass] 10 | public class BlockTest 11 | { 12 | byte[] someBytes = new byte[] { 1, 2, 3 }; 13 | 14 | [TestMethod] 15 | public void DataBytes() 16 | { 17 | var block = new Block 18 | { 19 | DataBytes = someBytes 20 | }; 21 | CollectionAssert.AreEqual(someBytes, block.DataBytes); 22 | } 23 | 24 | [TestMethod] 25 | public void DataStream() 26 | { 27 | var block = new Block 28 | { 29 | DataBytes = someBytes 30 | }; 31 | var stream = block.DataStream; 32 | Assert.AreEqual(1, stream.ReadByte()); 33 | Assert.AreEqual(2, stream.ReadByte()); 34 | Assert.AreEqual(3, stream.ReadByte()); 35 | Assert.AreEqual(-1, stream.ReadByte(), "at eof"); 36 | } 37 | 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /test/CoreApi/BitswapApiTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs.Http 10 | { 11 | 12 | [TestClass] 13 | public class BitswapApiTest 14 | { 15 | IpfsClient ipfs = TestFixture.Ipfs; 16 | 17 | [TestMethod] 18 | public async Task Wants() 19 | { 20 | var block = new DagNode(Encoding.UTF8.GetBytes("BitswapApiTest unknown block")); 21 | #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed 22 | Task.Run(() => ipfs.Bitswap.GetAsync(block.Id).Wait()); 23 | 24 | var endTime = DateTime.Now.AddSeconds(10); 25 | while (DateTime.Now < endTime) 26 | { 27 | await Task.Delay(100); 28 | var wants = await ipfs.Bitswap.WantsAsync(); 29 | if (wants.Contains(block.Id)) 30 | return; 31 | } 32 | Assert.Fail("wanted block is missing"); 33 | } 34 | 35 | [TestMethod] 36 | [Ignore("https://github.com/ipfs/go-ipfs/issues/5295")] 37 | public async Task Unwant() 38 | { 39 | var block = new DagNode(Encoding.UTF8.GetBytes("BitswapApiTest unknown block 2")); 40 | #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed 41 | Task.Run(() => ipfs.Bitswap.GetAsync(block.Id).Wait()); 42 | 43 | var endTime = DateTime.Now.AddSeconds(10); 44 | while (true) 45 | { 46 | if (DateTime.Now > endTime) 47 | Assert.Fail("wanted block is missing"); 48 | await Task.Delay(100); 49 | var wants = await ipfs.Bitswap.WantsAsync(); 50 | if (wants.Contains(block.Id)) 51 | break; 52 | } 53 | 54 | await ipfs.Bitswap.UnwantAsync(block.Id); 55 | endTime = DateTime.Now.AddSeconds(10); 56 | while (true) 57 | { 58 | if (DateTime.Now > endTime) 59 | Assert.Fail("unwanted block is present"); 60 | await Task.Delay(100); 61 | var wants = await ipfs.Bitswap.WantsAsync(); 62 | if (!wants.Contains(block.Id)) 63 | break; 64 | } 65 | } 66 | 67 | [TestMethod] 68 | public async Task Ledger() 69 | { 70 | var peer = new Peer { Id = "QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3" }; 71 | var ledger = await ipfs.Bitswap.LedgerAsync(peer); 72 | Assert.IsNotNull(ledger); 73 | Assert.AreEqual(peer.Id, ledger.Peer.Id); 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/CoreApi/BlockApiTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs.Http 10 | { 11 | 12 | [TestClass] 13 | public class BlockApiTest 14 | { 15 | IpfsClient ipfs = TestFixture.Ipfs; 16 | string id = "QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rAQ"; 17 | byte[] blob = Encoding.UTF8.GetBytes("blorb"); 18 | 19 | [TestMethod] 20 | public void Put_Bytes() 21 | { 22 | var cid = ipfs.Block.PutAsync(blob).Result; 23 | Assert.AreEqual(id, (string)cid); 24 | 25 | var data = ipfs.Block.GetAsync(cid).Result; 26 | Assert.AreEqual(blob.Length, data.Size); 27 | CollectionAssert.AreEqual(blob, data.DataBytes); 28 | } 29 | 30 | [TestMethod] 31 | public void Put_Bytes_ContentType() 32 | { 33 | var cid = ipfs.Block.PutAsync(blob, contentType: "raw").Result; 34 | Assert.AreEqual("bafkreiaxnnnb7qz2focittuqq3ya25q7rcv3bqynnczfzako47346wosmu", (string)cid); 35 | 36 | var data = ipfs.Block.GetAsync(cid).Result; 37 | Assert.AreEqual(blob.Length, data.Size); 38 | CollectionAssert.AreEqual(blob, data.DataBytes); 39 | } 40 | 41 | [TestMethod] 42 | public void Put_Bytes_Hash() 43 | { 44 | var cid = ipfs.Block.PutAsync(blob, "raw", "sha2-512").Result; 45 | Assert.AreEqual("bafkrgqelljziv4qfg5mefz36m2y3h6voaralnw6lwb4f53xcnrf4mlsykkn7vt6eno547tw5ygcz62kxrle45wnbmpbofo5tvu57jvuaf7k7e", (string)cid); 46 | 47 | var data = ipfs.Block.GetAsync(cid).Result; 48 | Assert.AreEqual(blob.Length, data.Size); 49 | CollectionAssert.AreEqual(blob, data.DataBytes); 50 | } 51 | 52 | [TestMethod] 53 | public void Put_Bytes_Pinned() 54 | { 55 | var data1 = new byte[] { 23, 24, 127 }; 56 | var cid1 = ipfs.Block.PutAsync(data1, contentType: "raw", pin: true).Result; 57 | var pins = ipfs.Pin.ListAsync().Result; 58 | Assert.IsTrue(pins.Any(pin => pin == cid1)); 59 | 60 | var data2 = new byte[] { 123, 124, 27 }; 61 | var cid2 = ipfs.Block.PutAsync(data2, contentType: "raw", pin: false).Result; 62 | pins = ipfs.Pin.ListAsync().Result; 63 | Assert.IsFalse(pins.Any(pin => pin == cid2)); 64 | } 65 | 66 | [TestMethod] 67 | public void Put_Stream() 68 | { 69 | var cid = ipfs.Block.PutAsync(new MemoryStream(blob)).Result; 70 | Assert.AreEqual(id, (string)cid); 71 | 72 | var data = ipfs.Block.GetAsync(cid).Result; 73 | Assert.AreEqual(blob.Length, data.Size); 74 | CollectionAssert.AreEqual(blob, data.DataBytes); 75 | } 76 | 77 | [TestMethod] 78 | public void Put_Stream_ContentType() 79 | { 80 | var cid = ipfs.Block.PutAsync(new MemoryStream(blob), contentType: "raw").Result; 81 | Assert.AreEqual("bafkreiaxnnnb7qz2focittuqq3ya25q7rcv3bqynnczfzako47346wosmu", (string)cid); 82 | 83 | var data = ipfs.Block.GetAsync(cid).Result; 84 | Assert.AreEqual(blob.Length, data.Size); 85 | CollectionAssert.AreEqual(blob, data.DataBytes); 86 | } 87 | 88 | [TestMethod] 89 | public void Put_Stream_Hash() 90 | { 91 | var cid = ipfs.Block.PutAsync(new MemoryStream(blob), "raw", "sha2-512").Result; 92 | Assert.AreEqual("bafkrgqelljziv4qfg5mefz36m2y3h6voaralnw6lwb4f53xcnrf4mlsykkn7vt6eno547tw5ygcz62kxrle45wnbmpbofo5tvu57jvuaf7k7e", (string)cid); 93 | 94 | var data = ipfs.Block.GetAsync(cid).Result; 95 | Assert.AreEqual(blob.Length, data.Size); 96 | CollectionAssert.AreEqual(blob, data.DataBytes); 97 | } 98 | 99 | [TestMethod] 100 | public void Put_Stream_Pinned() 101 | { 102 | var data1 = new MemoryStream(new byte[] { 23, 24, 127 }); 103 | var cid1 = ipfs.Block.PutAsync(data1, contentType: "raw", pin: true).Result; 104 | var pins = ipfs.Pin.ListAsync().Result; 105 | Assert.IsTrue(pins.Any(pin => pin == cid1)); 106 | 107 | var data2 = new MemoryStream(new byte[] { 123, 124, 27 }); 108 | var cid2 = ipfs.Block.PutAsync(data2, contentType: "raw", pin: false).Result; 109 | pins = ipfs.Pin.ListAsync().Result; 110 | Assert.IsFalse(pins.Any(pin => pin == cid2)); 111 | } 112 | 113 | [TestMethod] 114 | public void Get() 115 | { 116 | var _ = ipfs.Block.PutAsync(blob).Result; 117 | var block = ipfs.Block.GetAsync(id).Result; 118 | Assert.AreEqual(id, (string)block.Id); 119 | CollectionAssert.AreEqual(blob, block.DataBytes); 120 | var blob1 = new byte[blob.Length]; 121 | block.DataStream.Read(blob1, 0, blob1.Length); 122 | CollectionAssert.AreEqual(blob, blob1); 123 | } 124 | 125 | [TestMethod] 126 | public void Stat() 127 | { 128 | var _ = ipfs.Block.PutAsync(blob).Result; 129 | var info = ipfs.Block.StatAsync(id).Result; 130 | Assert.AreEqual(id, (string)info.Id); 131 | Assert.AreEqual(5, info.Size); 132 | } 133 | 134 | [TestMethod] 135 | public async Task Remove() 136 | { 137 | var _ = ipfs.Block.PutAsync(blob).Result; 138 | var cid = await ipfs.Block.RemoveAsync(id); 139 | Assert.AreEqual(id, (string)cid); 140 | } 141 | 142 | [TestMethod] 143 | public void Remove_Unknown() 144 | { 145 | ExceptionAssert.Throws(() => { var _ = ipfs.Block.RemoveAsync("QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rFF").Result; }); 146 | } 147 | 148 | [TestMethod] 149 | public async Task Remove_Unknown_OK() 150 | { 151 | var cid = await ipfs.Block.RemoveAsync("QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rFF", true); 152 | } 153 | 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /test/CoreApi/BlockRepositoryTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Ipfs.Http 9 | { 10 | 11 | [TestClass] 12 | public class BlockRepositoryTest 13 | { 14 | 15 | [TestMethod] 16 | public async Task Stats() 17 | { 18 | var ipfs = TestFixture.Ipfs; 19 | var stats = await ipfs.BlockRepository.StatisticsAsync(); 20 | Assert.IsNotNull(stats); 21 | } 22 | 23 | [TestMethod] 24 | public async Task Version() 25 | { 26 | var ipfs = TestFixture.Ipfs; 27 | var version = await ipfs.BlockRepository.VersionAsync(); 28 | Assert.IsFalse(string.IsNullOrWhiteSpace(version)); 29 | } 30 | 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /test/CoreApi/BootstrapTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs.Http 10 | { 11 | 12 | [TestClass] 13 | public class BootstapApiTest 14 | { 15 | IpfsClient ipfs = TestFixture.Ipfs; 16 | MultiAddress somewhere = "/ip4/127.0.0.1/tcp/4009/ipfs/QmPv52ekjS75L4JmHpXVeuJ5uX2ecSfSZo88NSyxwA3rAQ"; 17 | 18 | [TestMethod] 19 | public async Task Add_Remove() 20 | { 21 | var addr = await ipfs.Bootstrap.AddAsync(somewhere); 22 | Assert.IsNotNull(addr); 23 | Assert.AreEqual(somewhere, addr); 24 | var addrs = await ipfs.Bootstrap.ListAsync(); 25 | Assert.IsTrue(addrs.Any(a => a == somewhere)); 26 | 27 | addr = await ipfs.Bootstrap.RemoveAsync(somewhere); 28 | Assert.IsNotNull(addr); 29 | Assert.AreEqual(somewhere, addr); 30 | addrs = await ipfs.Bootstrap.ListAsync(); 31 | Assert.IsFalse(addrs.Any(a => a == somewhere)); 32 | } 33 | 34 | [TestMethod] 35 | public async Task List() 36 | { 37 | var addrs = await ipfs.Bootstrap.ListAsync(); 38 | Assert.IsNotNull(addrs); 39 | Assert.AreNotEqual(0, addrs.Count()); 40 | } 41 | 42 | [TestMethod] 43 | public async Task Remove_All() 44 | { 45 | var original = await ipfs.Bootstrap.ListAsync(); 46 | await ipfs.Bootstrap.RemoveAllAsync(); 47 | var addrs = await ipfs.Bootstrap.ListAsync(); 48 | Assert.AreEqual(0, addrs.Count()); 49 | foreach (var addr in original) 50 | { 51 | await ipfs.Bootstrap.AddAsync(addr); 52 | } 53 | } 54 | 55 | [TestMethod] 56 | public async Task Add_Defaults() 57 | { 58 | var original = await ipfs.Bootstrap.ListAsync(); 59 | await ipfs.Bootstrap.RemoveAllAsync(); 60 | try 61 | { 62 | await ipfs.Bootstrap.AddDefaultsAsync(); 63 | var addrs = await ipfs.Bootstrap.ListAsync(); 64 | Assert.AreNotEqual(0, addrs.Count()); 65 | } 66 | finally 67 | { 68 | await ipfs.Bootstrap.RemoveAllAsync(); 69 | foreach (var addr in original) 70 | { 71 | await ipfs.Bootstrap.AddAsync(addr); 72 | } 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /test/CoreApi/CancellationTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Newtonsoft.Json.Linq; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs.Http 10 | { 11 | [TestClass] 12 | public class CancellationTest 13 | { 14 | [TestMethod] 15 | public async Task Cancel_Operation() 16 | { 17 | var ipfs = TestFixture.Ipfs; 18 | var cs = new CancellationTokenSource(500); 19 | try 20 | { 21 | await Task.Delay(1000); 22 | var result = await ipfs.IdAsync(cancel: cs.Token); 23 | Assert.Fail("Did not throw TaskCanceledException"); 24 | } 25 | catch (TaskCanceledException) 26 | { 27 | return; 28 | } 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/CoreApi/ConfigApiTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Ipfs.Http 9 | { 10 | 11 | [TestClass] 12 | public class ConfigApiTest 13 | { 14 | const string apiAddress = "/ip4/127.0.0.1/tcp/"; 15 | const string gatewayAddress = "/ip4/127.0.0.1/tcp/"; 16 | 17 | [TestMethod] 18 | public void Get_Entire_Config() 19 | { 20 | IpfsClient ipfs = TestFixture.Ipfs; 21 | var config = ipfs.Config.GetAsync().Result; 22 | StringAssert.StartsWith(config["Addresses"]["API"].Value(), apiAddress); 23 | } 24 | 25 | [TestMethod] 26 | public void Get_Scalar_Key_Value() 27 | { 28 | IpfsClient ipfs = TestFixture.Ipfs; 29 | var api = ipfs.Config.GetAsync("Addresses.API").Result; 30 | StringAssert.StartsWith(api.Value(), apiAddress); 31 | } 32 | 33 | [TestMethod] 34 | public void Get_Object_Key_Value() 35 | { 36 | IpfsClient ipfs = TestFixture.Ipfs; 37 | var addresses = ipfs.Config.GetAsync("Addresses").Result; 38 | StringAssert.StartsWith(addresses["API"].Value(), apiAddress); 39 | StringAssert.StartsWith(addresses["Gateway"].Value(), gatewayAddress); 40 | } 41 | 42 | [TestMethod] 43 | public void Keys_are_Case_Sensitive() 44 | { 45 | IpfsClient ipfs = TestFixture.Ipfs; 46 | var api = ipfs.Config.GetAsync("Addresses.API").Result; 47 | StringAssert.StartsWith(api.Value(), apiAddress); 48 | 49 | ExceptionAssert.Throws(() => { var x = ipfs.Config.GetAsync("Addresses.api").Result; }); 50 | } 51 | 52 | [TestMethod] 53 | public void Set_String_Value() 54 | { 55 | const string key = "foo"; 56 | const string value = "foobar"; 57 | IpfsClient ipfs = TestFixture.Ipfs; 58 | ipfs.Config.SetAsync(key, value).Wait(); 59 | Assert.AreEqual(value, ipfs.Config.GetAsync(key).Result); 60 | } 61 | 62 | [TestMethod] 63 | public void Set_JSON_Value() 64 | { 65 | const string key = "API.HTTPHeaders.Access-Control-Allow-Origin"; 66 | JToken value = JToken.Parse("['http://example.io']"); 67 | IpfsClient ipfs = TestFixture.Ipfs; 68 | ipfs.Config.SetAsync(key, value).Wait(); 69 | Assert.AreEqual("http://example.io", ipfs.Config.GetAsync(key).Result[0]); 70 | } 71 | 72 | [TestMethod] 73 | public async Task Replace_Entire_Config() 74 | { 75 | IpfsClient ipfs = TestFixture.Ipfs; 76 | var original = await ipfs.Config.GetAsync(); 77 | try 78 | { 79 | var a = JObject.Parse("{ \"foo-x-bar\": 1 }"); 80 | await ipfs.Config.ReplaceAsync(a); 81 | } 82 | finally 83 | { 84 | await ipfs.Config.ReplaceAsync(original); 85 | } 86 | } 87 | 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /test/CoreApi/DagApiTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.Http 8 | { 9 | [TestClass] 10 | public class DagApiTest 11 | { 12 | class Name 13 | { 14 | public string First { get; set; } 15 | public string Last { get; set; } 16 | } 17 | 18 | [TestMethod] 19 | public async Task PutAndGet_JSON() 20 | { 21 | var ipfs = TestFixture.Ipfs; 22 | var expected = new JObject(); 23 | expected["a"] = "alpha"; 24 | var expectedId = "bafyreigdhej736dobd6z3jt2vxsxvbwrwgyts7e7wms6yrr46rp72uh5bu"; 25 | var id = await ipfs.Dag.PutAsync(expected); 26 | Assert.IsNotNull(id); 27 | Assert.AreEqual(expectedId, (string)id); 28 | 29 | var actual = await ipfs.Dag.GetAsync(id); 30 | Assert.IsNotNull(actual); 31 | Assert.AreEqual(expected["a"], actual["a"]); 32 | 33 | var value = (string) await ipfs.Dag.GetAsync(expectedId + "/a"); 34 | Assert.AreEqual(expected["a"], value); 35 | } 36 | 37 | [TestMethod] 38 | public async Task PutAndGet_POCO() 39 | { 40 | var ipfs = TestFixture.Ipfs; 41 | var expected = new Name { First = "John", Last = "Smith" }; 42 | var id = await ipfs.Dag.PutAsync(expected); 43 | Assert.IsNotNull(id); 44 | 45 | var actual = await ipfs.Dag.GetAsync(id); 46 | Assert.IsNotNull(actual); 47 | Assert.AreEqual(expected.First, actual.First); 48 | Assert.AreEqual(expected.Last, actual.Last); 49 | 50 | var value = (string)await ipfs.Dag.GetAsync(id.Encode() + "/Last"); 51 | Assert.AreEqual(expected.Last, value); 52 | } 53 | } 54 | } 55 | 56 | -------------------------------------------------------------------------------- /test/CoreApi/DhtApiTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs.Http 10 | { 11 | 12 | [TestClass] 13 | public class DhtApiTest 14 | { 15 | const string helloWorldID = "QmT78zSuBmuS4z925WZfrqQ1qHaJ56DQaTfyMUF7F8ff5o"; 16 | 17 | [TestMethod] 18 | public async Task FindPeer() 19 | { 20 | var ipfs = TestFixture.Ipfs; 21 | var cts = new CancellationTokenSource(TimeSpan.FromMinutes(2)); 22 | var mars = await ipfs.Dht.FindPeerAsync("QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3", cts.Token); 23 | Assert.IsNotNull(mars); 24 | } 25 | 26 | [TestMethod] 27 | public async Task FindProviders() 28 | { 29 | var ipfs = TestFixture.Ipfs; 30 | var cts = new CancellationTokenSource(TimeSpan.FromMinutes(2)); 31 | var providers = await ipfs.Dht.FindProvidersAsync(helloWorldID, 1, cancel: cts.Token); 32 | Assert.AreNotEqual(0, providers.Count()); 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /test/CoreApi/DnsApiTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ipfs.Http 11 | { 12 | 13 | [TestClass] 14 | public class DnsApiTest 15 | { 16 | 17 | [TestMethod] 18 | public void Api_Exists() 19 | { 20 | IpfsClient ipfs = TestFixture.Ipfs; 21 | Assert.IsNotNull(ipfs.Dns); 22 | } 23 | 24 | [TestMethod] 25 | public async Task Resolve() 26 | { 27 | IpfsClient ipfs = TestFixture.Ipfs; 28 | var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30)); 29 | var path = await ipfs.Dns.ResolveAsync("ipfs.io", recursive: true, cancel: cts.Token); 30 | StringAssert.StartsWith(path, "/ipfs/"); 31 | } 32 | 33 | [TestMethod] 34 | [Ignore("takes forever")] 35 | public async Task Publish() 36 | { 37 | var ipfs = TestFixture.Ipfs; 38 | var cs = new CancellationTokenSource(TimeSpan.FromMinutes(5)); 39 | var content = await ipfs.FileSystem.AddTextAsync("hello world"); 40 | var key = await ipfs.Key.CreateAsync("name-publish-test", "rsa", 1024); 41 | try 42 | { 43 | var result = await ipfs.Name.PublishAsync(content.Id, key.Name, cancel: cs.Token); 44 | Assert.IsNotNull(result); 45 | StringAssert.EndsWith(result.NamePath, key.Id.ToString()); 46 | StringAssert.EndsWith(result.ContentPath, content.Id.Encode()); 47 | } 48 | finally 49 | { 50 | await ipfs.Key.RemoveAsync(key.Name); 51 | } 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /test/CoreApi/GenericApiTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System.Linq; 3 | using System; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.Http 8 | { 9 | [TestClass] 10 | public class GenericApiTest 11 | { 12 | [TestMethod] 13 | public void Local_Node_Info() 14 | { 15 | var ipfs = TestFixture.Ipfs; 16 | var node = ipfs.IdAsync().Result; 17 | Assert.IsInstanceOfType(node, typeof(Peer)); 18 | } 19 | 20 | [TestMethod] 21 | public async Task Peer_Node_Info() 22 | { 23 | var ipfs = TestFixture.Ipfs; 24 | var mars = await ipfs.IdAsync("QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"); 25 | Assert.IsNotNull(mars); 26 | } 27 | 28 | [TestMethod] 29 | public void Version_Info() 30 | { 31 | var ipfs = TestFixture.Ipfs; 32 | var versions = ipfs.VersionAsync().Result; 33 | Assert.IsNotNull(versions); 34 | Assert.IsTrue(versions.ContainsKey("Version")); 35 | Assert.IsTrue(versions.ContainsKey("Repo")); 36 | } 37 | 38 | [TestMethod] 39 | public void Resolve() 40 | { 41 | var ipfs = TestFixture.Ipfs; 42 | var path = ipfs.ResolveAsync("QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao").Result; 43 | Assert.AreEqual("/ipfs/QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao", path); 44 | } 45 | 46 | [TestMethod] 47 | public async Task Ping_Peer() 48 | { 49 | var ipfs = TestFixture.Ipfs; 50 | MultiHash peer = "QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; 51 | var actual = await ipfs.Generic.PingAsync(peer, count: 1); 52 | Assert.IsNotNull(actual); 53 | Assert.AreNotEqual(0, actual.Count()); 54 | } 55 | 56 | [TestMethod] 57 | public async Task Ping_Address() 58 | { 59 | var ipfs = TestFixture.Ipfs; 60 | MultiAddress addr = "/ip4/104.236.179.241/tcp/4001/ipfs/QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM"; 61 | var actual = await ipfs.Generic.PingAsync(addr, count: 1); 62 | Assert.IsNotNull(actual); 63 | Assert.AreNotEqual(0, actual.Count()); 64 | } 65 | } 66 | } 67 | 68 | -------------------------------------------------------------------------------- /test/CoreApi/KeyApiTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ipfs.Http 11 | { 12 | 13 | [TestClass] 14 | public class KeyApiTest 15 | { 16 | 17 | [TestMethod] 18 | public void Api_Exists() 19 | { 20 | IpfsClient ipfs = TestFixture.Ipfs; 21 | Assert.IsNotNull(ipfs.Key); 22 | } 23 | 24 | [TestMethod] 25 | public async Task Self_Key_Exists() 26 | { 27 | IpfsClient ipfs = TestFixture.Ipfs; 28 | var keys = await ipfs.Key.ListAsync(); 29 | var self = keys.Single(k => k.Name == "self"); 30 | var me = await ipfs.IdAsync(); 31 | Assert.AreEqual("self", self.Name); 32 | Assert.AreEqual(me.Id, self.Id); 33 | } 34 | 35 | [TestMethod] 36 | public async Task Create_RSA_Key() 37 | { 38 | var name = "net-api-test-create"; 39 | IpfsClient ipfs = TestFixture.Ipfs; 40 | var key = await ipfs.Key.CreateAsync(name, "rsa", 1024); 41 | try 42 | { 43 | Assert.IsNotNull(key); 44 | Assert.IsNotNull(key.Id); 45 | Assert.AreEqual(name, key.Name); 46 | 47 | var keys = await ipfs.Key.ListAsync(); 48 | var clone = keys.Single(k => k.Name == name); 49 | Assert.AreEqual(key.Name, clone.Name); 50 | Assert.AreEqual(key.Id, clone.Id); 51 | } 52 | finally 53 | { 54 | await ipfs.Key.RemoveAsync(name); 55 | } 56 | } 57 | 58 | [TestMethod] 59 | public async Task Remove_Key() 60 | { 61 | var name = "net-api-test-remove"; 62 | IpfsClient ipfs = TestFixture.Ipfs; 63 | var key = await ipfs.Key.CreateAsync(name, "rsa", 1024); 64 | var keys = await ipfs.Key.ListAsync(); 65 | var clone = keys.Single(k => k.Name == name); 66 | Assert.IsNotNull(clone); 67 | 68 | var removed = await ipfs.Key.RemoveAsync(name); 69 | Assert.IsNotNull(removed); 70 | Assert.AreEqual(key.Name, removed.Name); 71 | Assert.AreEqual(key.Id, removed.Id); 72 | 73 | keys = await ipfs.Key.ListAsync(); 74 | Assert.IsFalse(keys.Any(k => k.Name == name)); 75 | } 76 | 77 | [TestMethod] 78 | public async Task Rename_Key() 79 | { 80 | var oname = "net-api-test-rename1"; 81 | var rname = "net-api-test-rename2"; 82 | IpfsClient ipfs = TestFixture.Ipfs; 83 | var okey = await ipfs.Key.CreateAsync(oname, "rsa", 1024); 84 | try 85 | { 86 | Assert.AreEqual(oname, okey.Name); 87 | 88 | var rkey = await ipfs.Key.RenameAsync(oname, rname); 89 | Assert.AreEqual(okey.Id, rkey.Id); 90 | Assert.AreEqual(rname, rkey.Name); 91 | 92 | var keys = await ipfs.Key.ListAsync(); 93 | Assert.IsTrue(keys.Any(k => k.Name == rname)); 94 | Assert.IsFalse(keys.Any(k => k.Name == oname)); 95 | } 96 | finally 97 | { 98 | try 99 | { 100 | await ipfs.Key.RemoveAsync(oname); 101 | } 102 | catch (Exception) { } 103 | try 104 | { 105 | await ipfs.Key.RemoveAsync(rname); 106 | } 107 | catch (Exception) { } 108 | } 109 | } 110 | 111 | } 112 | } 113 | -------------------------------------------------------------------------------- /test/CoreApi/NameApiTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ipfs.Http 11 | { 12 | 13 | [TestClass] 14 | public class NameApiTest 15 | { 16 | 17 | [TestMethod] 18 | public void Api_Exists() 19 | { 20 | IpfsClient ipfs = TestFixture.Ipfs; 21 | Assert.IsNotNull(ipfs.Name); 22 | } 23 | 24 | [TestMethod] 25 | public async Task Resolve() 26 | { 27 | IpfsClient ipfs = TestFixture.Ipfs; 28 | var id = await ipfs.Name.ResolveAsync("ipfs.io", recursive: true); 29 | StringAssert.StartsWith(id, "/ipfs/"); 30 | } 31 | 32 | [TestMethod] 33 | [Ignore("takes forever")] 34 | public async Task Publish() 35 | { 36 | var ipfs = TestFixture.Ipfs; 37 | var cs = new CancellationTokenSource(TimeSpan.FromMinutes(5)); 38 | var content = await ipfs.FileSystem.AddTextAsync("hello world"); 39 | var key = await ipfs.Key.CreateAsync("name-publish-test", "rsa", 1024); 40 | try 41 | { 42 | var result = await ipfs.Name.PublishAsync(content.Id, key.Name, cancel: cs.Token); 43 | Assert.IsNotNull(result); 44 | StringAssert.EndsWith(result.NamePath, key.Id.ToString()); 45 | StringAssert.EndsWith(result.ContentPath, content.Id.Encode()); 46 | } 47 | finally 48 | { 49 | await ipfs.Key.RemoveAsync(key.Name); 50 | } 51 | } 52 | 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /test/CoreApi/ObjectApiTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ipfs.Http 11 | { 12 | 13 | [TestClass] 14 | public class ObjectApiTest 15 | { 16 | IpfsClient ipfs = TestFixture.Ipfs; 17 | 18 | [TestMethod] 19 | public async Task New_Template_Null() 20 | { 21 | var node = await ipfs.Object.NewAsync(); 22 | Assert.AreEqual("QmdfTbBqBPQ7VNxZEYEj14VmRuZBkqFbiwReogJgS1zR1n", (string)node.Id); 23 | } 24 | 25 | [TestMethod] 26 | public async Task New_Template_UnixfsDir() 27 | { 28 | var node = await ipfs.Object.NewAsync("unixfs-dir"); 29 | Assert.AreEqual("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn", (string)node.Id); 30 | 31 | node = await ipfs.Object.NewDirectoryAsync(); 32 | Assert.AreEqual("QmUNLLsPACCz1vLxQVkXqqLX5R1X345qqfHbsf67hvA3Nn", (string)node.Id); 33 | 34 | } 35 | 36 | [TestMethod] 37 | public async Task Put_Get_Dag() 38 | { 39 | var adata = Encoding.UTF8.GetBytes("alpha"); 40 | var bdata = Encoding.UTF8.GetBytes("beta"); 41 | var alpha = new DagNode(adata); 42 | var beta = new DagNode(bdata, new[] { alpha.ToLink() }); 43 | var x = await ipfs.Object.PutAsync(beta); 44 | var node = await ipfs.Object.GetAsync(x.Id); 45 | CollectionAssert.AreEqual(beta.DataBytes, node.DataBytes); 46 | Assert.AreEqual(beta.Links.Count(), node.Links.Count()); 47 | Assert.AreEqual(beta.Links.First().Id, node.Links.First().Id); 48 | Assert.AreEqual(beta.Links.First().Name, node.Links.First().Name); 49 | Assert.AreEqual(beta.Links.First().Size, node.Links.First().Size); 50 | } 51 | 52 | [TestMethod] 53 | public async Task Put_Get_Data() 54 | { 55 | var adata = Encoding.UTF8.GetBytes("alpha"); 56 | var bdata = Encoding.UTF8.GetBytes("beta"); 57 | var alpha = new DagNode(adata); 58 | var beta = await ipfs.Object.PutAsync(bdata, new[] { alpha.ToLink() }); 59 | var node = await ipfs.Object.GetAsync(beta.Id); 60 | CollectionAssert.AreEqual(beta.DataBytes, node.DataBytes); 61 | Assert.AreEqual(beta.Links.Count(), node.Links.Count()); 62 | Assert.AreEqual(beta.Links.First().Id, node.Links.First().Id); 63 | Assert.AreEqual(beta.Links.First().Name, node.Links.First().Name); 64 | Assert.AreEqual(beta.Links.First().Size, node.Links.First().Size); 65 | } 66 | 67 | [TestMethod] 68 | public async Task Data() 69 | { 70 | var adata = Encoding.UTF8.GetBytes("alpha"); 71 | var node = await ipfs.Object.PutAsync(adata); 72 | using (var stream = await ipfs.Object.DataAsync(node.Id)) 73 | { 74 | var bdata = new byte[adata.Length]; 75 | stream.Read(bdata, 0, bdata.Length); 76 | CollectionAssert.AreEqual(adata, bdata); 77 | } 78 | } 79 | 80 | [TestMethod] 81 | public async Task Links() 82 | { 83 | var adata = Encoding.UTF8.GetBytes("alpha"); 84 | var bdata = Encoding.UTF8.GetBytes("beta"); 85 | var alpha = new DagNode(adata); 86 | var beta = await ipfs.Object.PutAsync(bdata, new[] { alpha.ToLink() }); 87 | var links = await ipfs.Object.LinksAsync(beta.Id); 88 | Assert.AreEqual(beta.Links.Count(),links.Count()); 89 | Assert.AreEqual(beta.Links.First().Id, links.First().Id); 90 | Assert.AreEqual(beta.Links.First().Name, links.First().Name); 91 | Assert.AreEqual(beta.Links.First().Size, links.First().Size); 92 | } 93 | 94 | [TestMethod] 95 | public async Task Stat() 96 | { 97 | var data1 = Encoding.UTF8.GetBytes("Some data 1"); 98 | var data2 = Encoding.UTF8.GetBytes("Some data 2"); 99 | var node2 = new DagNode(data2); 100 | var node1 = await ipfs.Object.PutAsync(data1, 101 | new[] { node2.ToLink("some-link") }); 102 | var info = await ipfs.Object.StatAsync(node1.Id); 103 | Assert.AreEqual(1, info.LinkCount); 104 | Assert.AreEqual(64, info.BlockSize); 105 | Assert.AreEqual(53, info.LinkSize); 106 | Assert.AreEqual(11, info.DataSize); 107 | Assert.AreEqual(77, info.CumulativeSize); 108 | } 109 | 110 | [TestMethod] 111 | public async Task Get_Nonexistent() 112 | { 113 | var data = Encoding.UTF8.GetBytes("Some data for net-ipfs-http-client-test that cannot be found"); 114 | var node = new DagNode(data); 115 | var id = node.Id; 116 | var cs = new CancellationTokenSource(500); 117 | try 118 | { 119 | var _ = await ipfs.Object.GetAsync(id, cs.Token); 120 | Assert.Fail("Did not throw TaskCanceledException"); 121 | } 122 | catch (TaskCanceledException) 123 | { 124 | return; 125 | } 126 | } 127 | 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /test/CoreApi/PinApiTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs.Http 10 | { 11 | 12 | [TestClass] 13 | public class PinApiTest 14 | { 15 | [TestMethod] 16 | public void List() 17 | { 18 | var ipfs = TestFixture.Ipfs; 19 | var pins = ipfs.Pin.ListAsync().Result; 20 | Assert.IsNotNull(pins); 21 | Assert.IsTrue(pins.Count() > 0); 22 | } 23 | 24 | [TestMethod] 25 | public async Task Add_Remove() 26 | { 27 | var ipfs = TestFixture.Ipfs; 28 | var result = await ipfs.FileSystem.AddTextAsync("I am pinned"); 29 | var id = result.Id; 30 | 31 | var pins = await ipfs.Pin.AddAsync(id); 32 | Assert.IsTrue(pins.Any(pin => pin == id)); 33 | var all = await ipfs.Pin.ListAsync(); 34 | Assert.IsTrue(all.Any(pin => pin == id)); 35 | 36 | pins = await ipfs.Pin.RemoveAsync(id); 37 | Assert.IsTrue(pins.Any(pin => pin == id)); 38 | all = await ipfs.Pin.ListAsync(); 39 | Assert.IsFalse(all.Any(pin => pin == id)); 40 | } 41 | 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/CoreApi/PubSubApiTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using Newtonsoft.Json.Linq; 4 | using System; 5 | using System.Collections.Generic; 6 | using System.IO; 7 | using System.Linq; 8 | using System.Text; 9 | using System.Threading; 10 | using System.Threading.Tasks; 11 | 12 | namespace Ipfs.Http 13 | { 14 | 15 | [TestClass] 16 | public class PubSubApiTest 17 | { 18 | 19 | [TestMethod] 20 | public void Api_Exists() 21 | { 22 | IpfsClient ipfs = TestFixture.Ipfs; 23 | Assert.IsNotNull(ipfs.PubSub); 24 | } 25 | 26 | [TestMethod] 27 | public async Task Peers() 28 | { 29 | var ipfs = TestFixture.Ipfs; 30 | var topic = "net-ipfs-http-client-test-" + Guid.NewGuid().ToString(); 31 | var cs = new CancellationTokenSource(); 32 | try 33 | { 34 | await ipfs.PubSub.SubscribeAsync(topic, msg => { }, cs.Token); 35 | var peers = ipfs.PubSub.PeersAsync().Result.ToArray(); 36 | Assert.IsTrue(peers.Length > 0); 37 | } 38 | finally 39 | { 40 | cs.Cancel(); 41 | } 42 | } 43 | 44 | [TestMethod] 45 | public void Peers_Unknown_Topic() 46 | { 47 | var ipfs = TestFixture.Ipfs; 48 | var topic = "net-ipfs-http-client-test-unknown" + Guid.NewGuid().ToString(); 49 | var peers = ipfs.PubSub.PeersAsync(topic).Result.ToArray(); 50 | Assert.AreEqual(0, peers.Length); 51 | } 52 | 53 | [TestMethod] 54 | public async Task Subscribed_Topics() 55 | { 56 | var ipfs = TestFixture.Ipfs; 57 | var topic = "net-ipfs-http-client-test-" + Guid.NewGuid().ToString(); 58 | var cs = new CancellationTokenSource(); 59 | try 60 | { 61 | await ipfs.PubSub.SubscribeAsync(topic, msg => { }, cs.Token); 62 | var topics = ipfs.PubSub.SubscribedTopicsAsync().Result.ToArray(); 63 | Assert.IsTrue(topics.Length > 0); 64 | CollectionAssert.Contains(topics, topic); 65 | } 66 | finally 67 | { 68 | cs.Cancel(); 69 | } 70 | } 71 | 72 | volatile int messageCount = 0; 73 | 74 | [TestMethod] 75 | public async Task Subscribe() 76 | { 77 | messageCount = 0; 78 | var ipfs = TestFixture.Ipfs; 79 | var topic = "net-ipfs-http-client-test-" + Guid.NewGuid().ToString(); 80 | var cs = new CancellationTokenSource(); 81 | try 82 | { 83 | await ipfs.PubSub.SubscribeAsync(topic, msg => 84 | { 85 | Interlocked.Increment(ref messageCount); 86 | }, cs.Token); 87 | await ipfs.PubSub.PublishAsync(topic, "hello world!"); 88 | 89 | await Task.Delay(1000); 90 | Assert.AreEqual(1, messageCount); 91 | } 92 | finally 93 | { 94 | cs.Cancel(); 95 | } 96 | } 97 | 98 | [TestMethod] 99 | public async Task Subscribe_Mutiple_Messages() 100 | { 101 | messageCount = 0; 102 | var messages = "hello world this is pubsub".Split(); 103 | var ipfs = TestFixture.Ipfs; 104 | var topic = "net-ipfs-http-client-test-" + Guid.NewGuid().ToString(); 105 | var cs = new CancellationTokenSource(); 106 | try 107 | { 108 | await ipfs.PubSub.SubscribeAsync(topic, msg => 109 | { 110 | Interlocked.Increment(ref messageCount); 111 | }, cs.Token); 112 | foreach (var msg in messages) 113 | { 114 | await ipfs.PubSub.PublishAsync(topic, msg); 115 | } 116 | 117 | await Task.Delay(1000); 118 | Assert.AreEqual(messages.Length, messageCount); 119 | } 120 | finally 121 | { 122 | cs.Cancel(); 123 | } 124 | } 125 | 126 | [TestMethod] 127 | public async Task Multiple_Subscribe_Mutiple_Messages() 128 | { 129 | messageCount = 0; 130 | var messages = "hello world this is pubsub".Split(); 131 | var ipfs = TestFixture.Ipfs; 132 | var topic = "net-ipfs-http-client-test-" + Guid.NewGuid().ToString(); 133 | var cs = new CancellationTokenSource(); 134 | Action processMessage = (msg) => 135 | { 136 | Interlocked.Increment(ref messageCount); 137 | }; 138 | try 139 | { 140 | await ipfs.PubSub.SubscribeAsync(topic, processMessage, cs.Token); 141 | await ipfs.PubSub.SubscribeAsync(topic, processMessage, cs.Token); 142 | foreach (var msg in messages) 143 | { 144 | await ipfs.PubSub.PublishAsync(topic, msg); 145 | } 146 | 147 | await Task.Delay(1000); 148 | Assert.AreEqual(messages.Length * 2, messageCount); 149 | } 150 | finally 151 | { 152 | cs.Cancel(); 153 | } 154 | } 155 | 156 | volatile int messageCount1 = 0; 157 | 158 | [TestMethod] 159 | public async Task Unsubscribe() 160 | { 161 | messageCount1 = 0; 162 | var ipfs = TestFixture.Ipfs; 163 | var topic = "net-ipfs-http-client-test-" + Guid.NewGuid().ToString(); 164 | var cs = new CancellationTokenSource(); 165 | await ipfs.PubSub.SubscribeAsync(topic, msg => 166 | { 167 | Interlocked.Increment(ref messageCount1); 168 | }, cs.Token); 169 | await ipfs.PubSub.PublishAsync(topic, "hello world!"); 170 | await Task.Delay(1000); 171 | Assert.AreEqual(1, messageCount1); 172 | 173 | cs.Cancel(); 174 | await ipfs.PubSub.PublishAsync(topic, "hello world!!!"); 175 | await Task.Delay(1000); 176 | Assert.AreEqual(1, messageCount1); 177 | } 178 | 179 | [TestMethod] 180 | public async Task Subscribe_BinaryMessage() 181 | { 182 | var messages = new List(); 183 | var expected = new byte[] { 0, 1, 2, 4, (byte)'a', (byte)'b', 0xfe, 0xff }; 184 | var ipfs = TestFixture.Ipfs; 185 | var topic = "net-ipfs-http-client-test-" + Guid.NewGuid().ToString(); 186 | var cs = new CancellationTokenSource(); 187 | try 188 | { 189 | await ipfs.PubSub.SubscribeAsync(topic, msg => 190 | { 191 | messages.Add(msg); 192 | }, cs.Token); 193 | await ipfs.PubSub.PublishAsync(topic, expected); 194 | 195 | await Task.Delay(1000); 196 | Assert.AreEqual(1, messages.Count); 197 | CollectionAssert.AreEqual(expected, messages[0].DataBytes); 198 | } 199 | finally 200 | { 201 | cs.Cancel(); 202 | } 203 | } 204 | 205 | [TestMethod] 206 | public async Task Subscribe_StreamMessage() 207 | { 208 | var messages = new List(); 209 | var expected = new byte[] { 0, 1, 2, 4, (byte)'a', (byte)'b', 0xfe, 0xff }; 210 | var ipfs = TestFixture.Ipfs; 211 | var topic = "net-ipfs-http-client-test-" + Guid.NewGuid().ToString(); 212 | var cs = new CancellationTokenSource(); 213 | try 214 | { 215 | await ipfs.PubSub.SubscribeAsync(topic, msg => 216 | { 217 | messages.Add(msg); 218 | }, cs.Token); 219 | var ms = new MemoryStream(expected, false); 220 | await ipfs.PubSub.PublishAsync(topic, ms); 221 | 222 | await Task.Delay(1000); 223 | Assert.AreEqual(1, messages.Count); 224 | CollectionAssert.AreEqual(expected, messages[0].DataBytes); 225 | } 226 | finally 227 | { 228 | cs.Cancel(); 229 | } 230 | } 231 | } 232 | } 233 | -------------------------------------------------------------------------------- /test/CoreApi/StatsApiTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Ipfs.Http 9 | { 10 | 11 | [TestClass] 12 | public class StatsApiTest 13 | { 14 | 15 | [TestMethod] 16 | public async Task SmokeTest() 17 | { 18 | var ipfs = TestFixture.Ipfs; 19 | var bandwidth = await ipfs.Stats.BandwidthAsync(); 20 | var bitswap = await ipfs.Stats.BitswapAsync(); 21 | var repository = await ipfs.Stats.RepositoryAsync(); 22 | } 23 | 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /test/CoreApi/SwarmApiTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using Newtonsoft.Json.Linq; 3 | using System; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | 8 | namespace Ipfs.Http 9 | { 10 | 11 | [TestClass] 12 | public class SwarmApiTest 13 | { 14 | 15 | [TestMethod] 16 | public async Task Addresses() 17 | { 18 | var ipfs = TestFixture.Ipfs; 19 | var swarm = await ipfs.Swarm.AddressesAsync(); 20 | foreach (var peer in swarm) 21 | { 22 | Assert.IsNotNull(peer.Id); 23 | Assert.IsNotNull(peer.Addresses); 24 | Assert.AreNotEqual(0, peer.Addresses.Count()); 25 | } 26 | } 27 | 28 | [TestMethod] 29 | public async Task Peers() 30 | { 31 | var ipfs = TestFixture.Ipfs; 32 | var peers = await ipfs.Swarm.PeersAsync(); 33 | Assert.AreNotEqual(0, peers.Count()); 34 | foreach (var peer in peers) 35 | { 36 | Assert.IsNotNull(peer.Id); 37 | Assert.IsNotNull(peer.ConnectedAddress); 38 | } 39 | } 40 | 41 | [TestMethod] 42 | public async Task Peers_Info() 43 | { 44 | var ipfs = TestFixture.Ipfs; 45 | var peers = await ipfs.Swarm.PeersAsync(); 46 | await Task.WhenAll(peers 47 | .Where(p => p.Latency != TimeSpan.Zero) 48 | .OrderBy(p => p.Latency) 49 | .Take(1) 50 | .Select(async p => 51 | { 52 | var peer = await ipfs.IdAsync(p.Id); 53 | Assert.AreNotEqual("", peer.PublicKey); 54 | })); 55 | } 56 | 57 | [TestMethod] 58 | public async Task Connection() 59 | { 60 | var ipfs = TestFixture.Ipfs; 61 | var peers = await ipfs.Swarm.PeersAsync(); 62 | 63 | // Sometimes we cannot connect to a specific peer. This 64 | // tests that a connection can be made to at least one peer. 65 | foreach (var peer in peers.Take(2)) 66 | { 67 | try 68 | { 69 | await ipfs.Swarm.ConnectAsync(peer.ConnectedAddress); 70 | return; 71 | } 72 | catch (Exception) 73 | { 74 | // eat it 75 | } 76 | } 77 | 78 | Assert.Fail("Cannot connect to any peer"); 79 | } 80 | 81 | [TestMethod] 82 | public async Task Filter_Add_Remove() 83 | { 84 | var ipfs = TestFixture.Ipfs; 85 | var somewhere = new MultiAddress("/ip4/192.127.0.0/ipcidr/16"); 86 | var filter = await ipfs.Swarm.AddAddressFilterAsync(somewhere, true); 87 | Assert.IsNotNull(filter); 88 | Assert.AreEqual(somewhere, filter); 89 | var filters = await ipfs.Swarm.ListAddressFiltersAsync(); 90 | Assert.IsTrue(filters.Any(a => a == somewhere)); 91 | filters = await ipfs.Swarm.ListAddressFiltersAsync(true); 92 | Assert.IsTrue(filters.Any(a => a == somewhere)); 93 | 94 | filter = await ipfs.Swarm.RemoveAddressFilterAsync(somewhere, true); 95 | Assert.IsNotNull(filter); 96 | Assert.AreEqual(somewhere, filter); 97 | filters = await ipfs.Swarm.ListAddressFiltersAsync(); 98 | Assert.IsFalse(filters.Any(a => a == somewhere)); 99 | filters = await ipfs.Swarm.ListAddressFiltersAsync(true); 100 | Assert.IsFalse(filters.Any(a => a == somewhere)); 101 | } 102 | 103 | [TestMethod] 104 | public async Task Filter_List() 105 | { 106 | var ipfs = TestFixture.Ipfs; 107 | var filters = await ipfs.Swarm.ListAddressFiltersAsync(persist: false); 108 | Assert.IsNotNull(filters); 109 | } 110 | 111 | [TestMethod] 112 | public async Task Filter_Remove_Unknown() 113 | { 114 | var ipfs = TestFixture.Ipfs; 115 | var somewhere = new MultiAddress("/ip4/192.168.0.3/ipcidr/2"); 116 | 117 | var filter = await ipfs.Swarm.RemoveAddressFilterAsync(somewhere); 118 | Assert.IsNull(filter); 119 | } 120 | } 121 | } 122 | -------------------------------------------------------------------------------- /test/ExceptionAssert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Ipfs.Http 6 | { 7 | /// 8 | /// Asserting an . 9 | /// 10 | public static class ExceptionAssert 11 | { 12 | 13 | public static T Throws(Action action, string expectedMessage = null) where T : Exception 14 | { 15 | try 16 | { 17 | action(); 18 | } 19 | catch (AggregateException e) 20 | { 21 | var match = e.InnerExceptions.OfType().FirstOrDefault(); 22 | if (match != null) 23 | { 24 | if (expectedMessage != null) 25 | Assert.AreEqual(expectedMessage, match.Message, "Wrong exception message."); 26 | return match; 27 | } 28 | 29 | throw; 30 | } 31 | catch (T e) 32 | { 33 | if (expectedMessage != null) 34 | Assert.AreEqual(expectedMessage, e.Message); 35 | return e; 36 | } 37 | Assert.Fail("Exception of type {0} should be thrown.", typeof(T)); 38 | 39 | // The compiler doesn't know that Assert.Fail will always throw an exception 40 | return null; 41 | } 42 | 43 | public static Exception Throws(Action action, string expectedMessage = null) 44 | { 45 | return Throws(action, expectedMessage); 46 | } 47 | 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /test/FileSystemNodeTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.Linq; 5 | using System.IO; 6 | using System.Threading.Tasks; 7 | using Newtonsoft.Json; 8 | 9 | namespace Ipfs.Http 10 | { 11 | 12 | [TestClass] 13 | public class FileSystemNodeTest 14 | { 15 | [TestMethod] 16 | public async Task Serialization() 17 | { 18 | var ipfs = TestFixture.Ipfs; 19 | var a = await ipfs.FileSystem.AddTextAsync("hello world"); 20 | Assert.AreEqual("Qmf412jQZiuVUtdgnB36FXFX7xg5V6KEbSJ4dpQuhkLyfD", (string)a.Id); 21 | 22 | var b = await ipfs.FileSystem.ListFileAsync(a.Id); 23 | var json = JsonConvert.SerializeObject(b); 24 | var c = JsonConvert.DeserializeObject(json); 25 | Assert.AreEqual(b.Id, c.Id); 26 | Assert.AreEqual(b.IsDirectory, c.IsDirectory); 27 | Assert.AreEqual(b.Size, c.Size); 28 | CollectionAssert.AreEqual(b.Links.ToArray(), c.Links.ToArray()); 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /test/IpfsClientTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.Net.Http; 5 | using System.Threading; 6 | 7 | namespace Ipfs.Http 8 | { 9 | 10 | /// 11 | ///This is a test class for IpfsClientTest and is intended 12 | ///to contain all IpfsClientTest Unit Tests 13 | /// 14 | [TestClass] 15 | public partial class IpfsClientTest 16 | { 17 | /// 18 | /// A test for IpfsClient Constructor 19 | /// 20 | [TestMethod] 21 | public void Can_Create() 22 | { 23 | IpfsClient target = TestFixture.Ipfs; 24 | Assert.IsNotNull(target); 25 | } 26 | 27 | [TestMethod] 28 | public void Do_Command_Throws_Exception_On_Invalid_Command() 29 | { 30 | IpfsClient target = TestFixture.Ipfs; 31 | object unknown; 32 | ExceptionAssert.Throws(() => unknown = target.DoCommandAsync("foobar", default(CancellationToken)).Result); 33 | } 34 | 35 | [TestMethod] 36 | public void Do_Command_Throws_Exception_On_Missing_Argument() 37 | { 38 | IpfsClient target = TestFixture.Ipfs; 39 | object unknown; 40 | ExceptionAssert.Throws(() => unknown = target.DoCommandAsync("key/gen", default(CancellationToken)).Result); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /test/IpfsHttpClientTests.csproj: -------------------------------------------------------------------------------- 1 |  2 | 3 | 4 | net45;netcoreapp1.1;netcoreapp2.0 5 | 6 | false 7 | full 8 | Ipfs.Http 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /test/MerkleNodeTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.Linq; 5 | using System.IO; 6 | 7 | namespace Ipfs.Http 8 | { 9 | 10 | [TestClass] 11 | public partial class MerkleNodeTest 12 | { 13 | const string IpfsInfo = "QmVtU7ths96fMgZ8YSZAbKghyieq7AjxNdcqyVzxTt3qVe"; 14 | 15 | [TestMethod] 16 | public void HashWithNamespace() 17 | { 18 | var node = new MerkleNode("/ipfs/" + IpfsInfo); 19 | Assert.AreEqual(IpfsInfo, (string)node.Id); 20 | } 21 | 22 | [TestMethod] 23 | public void Stringify() 24 | { 25 | var node = new MerkleNode(IpfsInfo); 26 | Assert.AreEqual("/ipfs/" + IpfsInfo, node.ToString()); 27 | } 28 | 29 | [TestMethod] 30 | public void FromString() 31 | { 32 | var a = new MerkleNode(IpfsInfo); 33 | var b = (MerkleNode)IpfsInfo; 34 | Assert.AreEqual(a, b); 35 | } 36 | 37 | [TestMethod] 38 | public void NullHash() 39 | { 40 | ExceptionAssert.Throws(() => new MerkleNode((string)null)); 41 | ExceptionAssert.Throws(() => new MerkleNode("")); 42 | ExceptionAssert.Throws(() => new MerkleNode((Cid)null)); 43 | } 44 | 45 | [TestMethod] 46 | public void FromALink() 47 | { 48 | var node = new MerkleNode(IpfsInfo); 49 | var link = new MerkleNode(node.Links.First()); 50 | Assert.AreEqual(link.Id, node.Links.First().Id); 51 | Assert.AreEqual(link.Name, node.Links.First().Name); 52 | Assert.AreEqual(link.BlockSize, node.Links.First().Size); 53 | } 54 | 55 | [TestMethod] 56 | public void ToALink() 57 | { 58 | var node = new MerkleNode(IpfsInfo); 59 | var link = node.ToLink(); 60 | Assert.AreEqual(link.Id, node.Id); 61 | Assert.AreEqual(link.Name, node.Name); 62 | Assert.AreEqual(link.Size, node.BlockSize); 63 | 64 | } 65 | 66 | [TestMethod] 67 | public void Value_Equality() 68 | { 69 | var a0 = new MerkleNode("QmStfpa7ppKPSsdnazBy3Q5QH4zNzGLcpWV88otjVSV7SY"); 70 | var a1 = new MerkleNode("QmStfpa7ppKPSsdnazBy3Q5QH4zNzGLcpWV88otjVSV7SY"); 71 | var b = new MerkleNode("QmagNHT6twJRBZcGeviiGzHVTMbNnJZameLyL6T14GUHCS"); 72 | MerkleNode nullNode = null; 73 | 74 | #pragma warning disable 1718 75 | Assert.IsTrue(a0 == a0); 76 | Assert.IsTrue(a0 == a1); 77 | Assert.IsFalse(a0 == b); 78 | Assert.IsFalse(a0 == null); 79 | 80 | #pragma warning disable 1718 81 | Assert.IsFalse(a0 != a0); 82 | Assert.IsFalse(a0 != a1); 83 | Assert.IsTrue(a0 != b); 84 | Assert.IsTrue(a0 != null); 85 | 86 | Assert.IsTrue(a0.Equals(a0)); 87 | Assert.IsTrue(a0.Equals(a1)); 88 | Assert.IsFalse(a0.Equals(b)); 89 | Assert.IsFalse(a0.Equals(null)); 90 | 91 | Assert.AreEqual(a0, a0); 92 | Assert.AreEqual(a0, a1); 93 | Assert.AreNotEqual(a0, b); 94 | Assert.AreNotEqual(a0, null); 95 | 96 | Assert.AreEqual(a0, a0); 97 | Assert.AreEqual(a0, a1); 98 | Assert.AreNotEqual(a0, b); 99 | Assert.AreNotEqual(a0, null); 100 | 101 | Assert.AreEqual(a0.GetHashCode(), a0.GetHashCode()); 102 | Assert.AreEqual(a0.GetHashCode(), a1.GetHashCode()); 103 | Assert.AreNotEqual(a0.GetHashCode(), b.GetHashCode()); 104 | 105 | Assert.IsTrue(nullNode == null); 106 | Assert.IsFalse(null == a0); 107 | Assert.IsFalse(nullNode != null); 108 | Assert.IsTrue(null != a0); 109 | } 110 | 111 | [TestMethod] 112 | public void DataBytes() 113 | { 114 | var node = new MerkleNode(IpfsInfo); 115 | byte[] data = node.DataBytes; 116 | Assert.AreEqual(node.BlockSize, data.Length); 117 | } 118 | 119 | [TestMethod] 120 | public void DataStream() 121 | { 122 | var node = new MerkleNode(IpfsInfo); 123 | byte[] data = node.DataBytes; 124 | var streamData = new MemoryStream(); 125 | node.DataStream.CopyTo(streamData); 126 | CollectionAssert.AreEqual(data, streamData.ToArray()); 127 | } 128 | 129 | } 130 | } 131 | -------------------------------------------------------------------------------- /test/PublishedMessageTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.Linq; 5 | using System.IO; 6 | 7 | namespace Ipfs.Http 8 | { 9 | 10 | [TestClass] 11 | public partial class PublishedMessageTest 12 | { 13 | const string json = @"{ 14 | ""from"":""EiDzOYdzT4BE42JXwxVM8Q19w6tx30Bp2N3T7tOH/a2nCw=="", 15 | ""data"":""aGVsbG8gd29ybGQ="", 16 | ""seqno"":""FPBVj+oTUug="", 17 | ""topicIDs"":[""net-ipfs-http-client-test""] 18 | }"; 19 | 20 | [TestMethod] 21 | public void FromJson() 22 | { 23 | var msg = new PublishedMessage(json); 24 | Assert.AreEqual("Qmei6fBYij8gjbetgHLXmoR54iRc9hioPR7dtmBTNG3oWa", msg.Sender); 25 | Assert.AreEqual("14f0558fea1352e8", msg.SequenceNumber.ToHexString()); 26 | Assert.AreEqual("68656c6c6f20776f726c64", msg.DataBytes.ToHexString()); 27 | Assert.AreEqual("hello world", msg.DataString); 28 | CollectionAssert.Contains(msg.Topics.ToArray(), "net-ipfs-http-client-test"); 29 | 30 | var data = msg.DataBytes; 31 | var streamData = new MemoryStream(); 32 | msg.DataStream.CopyTo(streamData); 33 | CollectionAssert.AreEqual(data, streamData.ToArray()); 34 | } 35 | 36 | [TestMethod] 37 | public void Id_NotSupported() 38 | { 39 | var msg = new PublishedMessage(json); 40 | ExceptionAssert.Throws(() => { 41 | var _ = msg.Id; 42 | }); 43 | } 44 | 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /test/TestFixture.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.Http 8 | { 9 | public static class TestFixture 10 | { 11 | /// 12 | /// Fiddler cannot see localhost traffic because .Net bypasses the network stack for localhost/127.0.0.1. 13 | /// By using "127.0.0.1." (note trailing dot) fiddler will receive the traffic and if its not running 14 | /// the localhost will get it! 15 | /// 16 | //IpfsClient.DefaultApiUri = new Uri("http://127.0.0.1.:5001"); 17 | 18 | public static IpfsClient Ipfs = new IpfsClient(); 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /test/TrustedPeersTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.Http; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.Linq; 5 | 6 | namespace Ipfs.Http 7 | { 8 | 9 | public partial class IpfsClientTest 10 | { 11 | MultiAddress newTrustedPeer = new MultiAddress("/ip4/25.196.147.100/tcp/4001/ipfs/QmaMqSwWShsPg2RbredZtoneFjXhim7AQkqbLxib45Lx4S"); 12 | 13 | [TestMethod] 14 | public void Trusted_Peers_List() 15 | { 16 | var ipfs = TestFixture.Ipfs; 17 | Assert.IsNotNull(ipfs.TrustedPeers); 18 | Assert.IsTrue(ipfs.TrustedPeers.Count > 0); 19 | } 20 | 21 | [TestMethod] 22 | public void Trusted_Peers_Add_Remove() 23 | { 24 | var ipfs = TestFixture.Ipfs; 25 | Assert.IsFalse(ipfs.TrustedPeers.Contains(newTrustedPeer)); 26 | 27 | ipfs.TrustedPeers.Add(newTrustedPeer); 28 | Assert.IsTrue(ipfs.TrustedPeers.Contains(newTrustedPeer)); 29 | 30 | ipfs.TrustedPeers.Remove(newTrustedPeer); 31 | Assert.IsFalse(ipfs.TrustedPeers.Contains(newTrustedPeer)); 32 | } 33 | 34 | // js-ipfs does NOT check IPFS addresses. 35 | // And this bad addr eventually breaks the server. 36 | // https://github.com/ipfs/js-ipfs/issues/1066 37 | #if false 38 | [TestMethod] 39 | public void Trusted_Peers_Add_Missing_Peer_ID() 40 | { 41 | var missingPeerId = new MultiAddress("/ip4/25.196.147.100/tcp/4001"); 42 | var ipfs = TestFixture.Ipfs; 43 | ExceptionAssert.Throws(() => ipfs.TrustedPeers.Add(missingPeerId), "invalid IPFS address"); 44 | } 45 | #endif 46 | 47 | [TestMethod] 48 | public void Trusted_Peers_Clear() 49 | { 50 | var ipfs = TestFixture.Ipfs; 51 | var original = ipfs.TrustedPeers.ToArray(); 52 | 53 | ipfs.TrustedPeers.Clear(); 54 | Assert.AreEqual(0, ipfs.TrustedPeers.Count); 55 | 56 | foreach (var a in original) 57 | ipfs.TrustedPeers.Add(a); 58 | } 59 | 60 | [TestMethod] 61 | public void Trusted_Peers_Add_Default_Nodes() 62 | { 63 | var ipfs = TestFixture.Ipfs; 64 | var original = ipfs.TrustedPeers.ToArray(); 65 | 66 | ipfs.TrustedPeers.Clear(); 67 | Assert.AreEqual(0, ipfs.TrustedPeers.Count); 68 | ipfs.TrustedPeers.AddDefaultNodes(); 69 | Assert.AreNotEqual(0, ipfs.TrustedPeers.Count); 70 | 71 | ipfs.TrustedPeers.Clear(); 72 | foreach (var a in original) 73 | ipfs.TrustedPeers.Add(a); 74 | } 75 | } 76 | } 77 | --------------------------------------------------------------------------------