├── GitVersionConfig.yaml ├── src ├── ipfs.dev.snk ├── ipfs.ci.snk.enc ├── IFileSystemLink.cs ├── ILinkedNode.cs ├── IFileSystemNode.cs ├── CoreApi │ ├── TransferProgress.cs │ ├── IDhtApi.cs │ ├── PingResult.cs │ ├── BandwidthData.cs │ ├── ObjectStat.cs │ ├── IPeerRouting.cs │ ├── RepositoryData.cs │ ├── BitswapData.cs │ ├── IDnsApi.cs │ ├── IStatsApi.cs │ ├── BitswapLedger.cs │ ├── IContentRouting.cs │ ├── IValueStore.cs │ ├── IBlockRepository.cs │ ├── IPinApi.cs │ ├── IConfigApi.cs │ ├── AddFileOptions.cs │ ├── IBootstrapApi.cs │ ├── IBitswapApi.cs │ ├── INameApi.cs │ ├── ICoreApi.cs │ ├── IPubSubApi.cs │ ├── IKeyApi.cs │ └── ISwarmApi.cs ├── NamedContent.cs ├── Base32z.cs ├── IKey.cs ├── Cryptography │ ├── DoubleSha256.cs │ ├── IdentityHash.cs │ ├── BouncyDigest.cs │ └── Keccak.cs ├── IMerkleLink.cs ├── ProtobufHelper.cs ├── IPublishedMessage.cs ├── IDataBlock.cs ├── IMerkleNode.cs ├── Base58.cs ├── Base64NoPad.cs ├── Base32.cs ├── IpfsCore.csproj ├── Base64Url.cs ├── MultiCodec.cs ├── MultiBase.cs ├── DagLink.cs ├── HexString.cs └── Registry │ └── Codec.cs ├── doc ├── api │ └── .gitignore ├── images │ ├── ipfs-favicon.ico │ ├── ipfs-cs-logo-48x48.png │ ├── ipfs-cs-logo-64x64.png │ ├── docs-latest-green.svg │ └── ipfs-logo.svg ├── toc.yml ├── Documentation.csproj ├── .gitignore ├── articles │ ├── toc.yml │ ├── multiaddress.md │ ├── cid.md │ ├── intro.md │ ├── varint.md │ ├── dag.md │ ├── core-api.md │ └── multihash.md ├── index.md ├── Properties │ └── AssemblyInfo.cs └── docfx.json ├── .codacy.yml ├── test ├── packages.config ├── NamedContentTest.cs ├── CoreApi │ ├── PingResultTest.cs │ ├── ObjectStatTest.cs │ ├── BitswapLedgerTest.cs │ └── AddFileOptionsTest.cs ├── App.config ├── IpfsCoreTests.csproj ├── ExceptionAssert.cs ├── Registry │ ├── CodecTest.cs │ ├── MultibaseAlgorithmTest.cs │ └── HashingAlgorithmTest.cs ├── NetworkProtocolTest.cs ├── Base58Test.cs ├── HexStringTest.cs ├── Base32Test.cs ├── DagLinkTest.cs ├── DurationTest.cs ├── VarintTest.cs └── PeerTest.cs ├── .travis.yml ├── Local.testsettings ├── IpfsCore.vsmdi ├── LICENSE ├── TraceAndTestImpact.testsettings ├── IpfsCore.sln ├── appveyor.yml ├── .gitignore └── README.md /GitVersionConfig.yaml: -------------------------------------------------------------------------------- 1 | mode: ContinuousDeployment 2 | next-version: 0.2.1 3 | branches: {} 4 | -------------------------------------------------------------------------------- /src/ipfs.dev.snk: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-core/HEAD/src/ipfs.dev.snk -------------------------------------------------------------------------------- /doc/api/.gitignore: -------------------------------------------------------------------------------- 1 | ############### 2 | # temp file # 3 | ############### 4 | *.yml 5 | .manifest 6 | -------------------------------------------------------------------------------- /src/ipfs.ci.snk.enc: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-core/HEAD/src/ipfs.ci.snk.enc -------------------------------------------------------------------------------- /.codacy.yml: -------------------------------------------------------------------------------- 1 | --- 2 | exclude_paths: 3 | - 'test/*' 4 | - 'test/**/*' 5 | - 'doc/*' 6 | - 'doc/**/*' 7 | -------------------------------------------------------------------------------- /doc/images/ipfs-favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-core/HEAD/doc/images/ipfs-favicon.ico -------------------------------------------------------------------------------- /doc/images/ipfs-cs-logo-48x48.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-core/HEAD/doc/images/ipfs-cs-logo-48x48.png -------------------------------------------------------------------------------- /doc/images/ipfs-cs-logo-64x64.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/richardschneider/net-ipfs-core/HEAD/doc/images/ipfs-cs-logo-64x64.png -------------------------------------------------------------------------------- /test/packages.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /doc/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Articles 2 | href: articles/ 3 | - name: Api Documentation 4 | href: api/ 5 | - name: Github 6 | href: https://github.com/richardschneider/net-ipfs-core 7 | -------------------------------------------------------------------------------- /doc/Documentation.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard2 5 | 6 | 7 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/IFileSystemLink.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs 6 | { 7 | /// 8 | /// A link to another file system node in IPFS. 9 | /// 10 | public interface IFileSystemLink : IMerkleLink 11 | { 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: csharp 2 | sudo: required 3 | mono: none 4 | dist: xenial 5 | dotnet: 3.0.100 6 | os: 7 | - linux 8 | - osx 9 | 10 | script: 11 | - dotnet restore 12 | - dotnet build -c Release --no-restore --framework netcoreapp3.0 ./src 13 | - dotnet test -c Release --no-restore --framework netcoreapp3.0 ./test -------------------------------------------------------------------------------- /doc/articles/toc.yml: -------------------------------------------------------------------------------- 1 | - name: Introduction 2 | href: intro.md 3 | - name: Core API 4 | href: core-api.md 5 | - name: CID 6 | href: cid.md 7 | - name: Merkle DAG 8 | href: dag.md 9 | - name: MultiAddress 10 | href: multiaddress.md 11 | - name: MultiHash 12 | href: multihash.md 13 | - name: Varint 14 | href: varint.md 15 | - name: Class Reference 16 | href: ../api/Ipfs.yml 17 | -------------------------------------------------------------------------------- /doc/articles/multiaddress.md: -------------------------------------------------------------------------------- 1 | # MultiAddress 2 | 3 | A standard way to represent a networks address that supports [multiple network protocols](xref:Ipfs.MultiAddress). It is represented as 4 | a series of tuples, a protocol code and an optional value. For example, an IPFS node at a sepcific address over ipv4 and tcp is 5 | 6 | /ip4/10.1.10.10/tcp/80/p2p/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC 7 | 8 | -------------------------------------------------------------------------------- /Local.testsettings: -------------------------------------------------------------------------------- 1 | 2 | 3 | These are default test settings for a local test run. 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /src/ILinkedNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs 6 | { 7 | /// 8 | /// InterPlanetary Linked Data. 9 | /// 10 | /// 11 | /// Not yet ready for prime time. 12 | /// 13 | /// 14 | public interface ILinkedNode : IMerkleNode 15 | { 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /IpfsCore.vsmdi: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | -------------------------------------------------------------------------------- /doc/articles/cid.md: -------------------------------------------------------------------------------- 1 | # Content ID 2 | 3 | A [Cid](xref:Ipfs.Cid) is a self-describing content-addressed identifier for distributed systems. It doesn't indicate *where* 4 | the content is stored, but it forms a kind of address based on the content itself. 5 | 6 | For background information, see [cid concept](https://docs.ipfs.io/guides/concepts/cid/). 7 | 8 | # Encoding 9 | 10 | In June 2019 the IPFS ecosystem switched from **base58btc** encoding 11 | to the case-insensitive **base32** encoding. This plays much 12 | better with DNS and URL based systems. 13 | -------------------------------------------------------------------------------- /doc/articles/intro.md: -------------------------------------------------------------------------------- 1 | # IPFS Core 2 | 3 | The core objects and interfaces of the [IPFS](https://ipfs.io) (Inter Planetary File System) for .Net (C#, VB, F# etc.) 4 | 5 | The interplanetary file system is the permanent web. It is a new hypermedia distribution protocol, addressed by content and identities. 6 | IPFS enables the creation of completely distributed applications. It aims to make the web faster, safer, and more open. 7 | 8 | The source code is on [GitHub](https://github.com/richardschneider/net-ipfs-core) and the 9 | package is published on [NuGet](https://www.nuget.org/packages/Ipfs.Core). 10 | -------------------------------------------------------------------------------- /doc/articles/varint.md: -------------------------------------------------------------------------------- 1 | # Variable Integer 2 | 3 | A [varint](xref:Ipfs.Varint) is used to encode a non-negative integer of up to 64 bits. 4 | It is encoded in network byte order (Big Endian). Each byte (except the last) contains 7 bits 5 | of information with the most significant bit set to 1. The last byte has the MSB set to 0. 6 | 7 | | Value | Varint encoding | 8 | | ----- | --------------- | 9 | | 1 (0x1) | 01 | 10 | | 16 (0x10) | 10 | 11 | | 256 (0x100) | 80 02 | 12 | | 4096 (0x1000) | 80 20 | 13 | | 65536 (0x10000) | 80 80 04 | 14 | | 1048576 (0x100000) | 80 80 40 | 15 | | 16777216 (0x1000000) | 80 80 80 08 | 16 | -------------------------------------------------------------------------------- /src/IFileSystemNode.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs 6 | { 7 | /// 8 | /// A Directed Acyclic Graph (DAG) for IPFS file system node. 9 | /// 10 | public interface IFileSystemNode : IMerkleNode 11 | { 12 | /// 13 | /// Determines if the node is a directory (folder). 14 | /// 15 | /// 16 | /// true if the node is a directory; Otherwise false, 17 | /// it is some type of a file. 18 | /// 19 | bool IsDirectory { get; } 20 | 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /doc/index.md: -------------------------------------------------------------------------------- 1 | # IPFS Core 2 | 3 | The [core objects](api/Ipfs.yml) of the [Inter Planetary File System](https://ipfs.io/) (IPFS) for .Net (C#, VB, F# etc.) 4 | The source code is on [GitHub](https://github.com/richardschneider/net-ipfs-core) and the 5 | package on [NuGet](https://www.nuget.org/packages/Ipfs.Core). 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 core objects 10 | - [API Documentation](api/Ipfs.yml) describes the core objects in detail -------------------------------------------------------------------------------- /test/NamedContentTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using System.IO; 7 | using Google.Protobuf; 8 | 9 | namespace Ipfs 10 | { 11 | [TestClass] 12 | public class NamedContentTest 13 | { 14 | [TestMethod] 15 | public void Properties() 16 | { 17 | var nc = new NamedContent 18 | { 19 | ContentPath = "/ipfs/...", 20 | NamePath = "/ipns/..." 21 | }; 22 | Assert.AreEqual("/ipfs/...", nc.ContentPath); 23 | Assert.AreEqual("/ipns/...", nc.NamePath); 24 | } 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /doc/images/docs-latest-green.svg: -------------------------------------------------------------------------------- 1 | docsdocslatestlatest -------------------------------------------------------------------------------- /test/CoreApi/PingResultTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Ipfs.CoreApi 7 | { 8 | [TestClass] 9 | public class PingResultTest 10 | { 11 | [TestMethod] 12 | public void Properties() 13 | { 14 | var time = TimeSpan.FromSeconds(3); 15 | var r = new PingResult 16 | { 17 | Success = true, 18 | Text = "ping", 19 | Time = time 20 | }; 21 | Assert.AreEqual(true, r.Success); 22 | Assert.AreEqual("ping", r.Text); 23 | Assert.AreEqual(time, r.Time); 24 | } 25 | 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /test/App.config: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 |
6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | -------------------------------------------------------------------------------- /src/CoreApi/TransferProgress.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs.CoreApi 6 | { 7 | /// 8 | /// Reports the progress of 9 | /// a transfer operation. 10 | /// 11 | public class TransferProgress 12 | { 13 | /// 14 | /// The name of the item being trasfered. 15 | /// 16 | /// 17 | /// Typically, a relative file path. 18 | /// 19 | public string Name; 20 | 21 | /// 22 | /// The cumuative number of bytes transfered for 23 | /// the . 24 | /// 25 | public ulong Bytes; 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/NamedContent.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs 6 | { 7 | /// 8 | /// Content that has an associated name. 9 | /// 10 | /// 11 | public class NamedContent 12 | { 13 | /// 14 | /// Path to the name. 15 | /// 16 | /// 17 | /// Typically /ipns/.... 18 | /// 19 | public string NamePath { get; set; } 20 | 21 | /// 22 | /// Path to the content. 23 | /// 24 | /// 25 | /// Typically /ipfs/.... 26 | /// 27 | public string ContentPath { get; set; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /src/CoreApi/IDhtApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.CoreApi 8 | { 9 | /// 10 | /// Manages the Distributed Hash Table. 11 | /// 12 | /// 13 | /// The DHT is a place to store, not the value, but pointers to peers who have 14 | /// the actual value. 15 | /// 16 | /// See the ongoing DHT specification at 17 | /// 18 | /// 19 | /// DHT API spec 20 | public interface IDhtApi : IPeerRouting, IContentRouting, IValueStore 21 | { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/CoreApi/PingResult.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs.CoreApi 6 | { 7 | /// 8 | /// The result from sending a . 9 | /// 10 | public class PingResult 11 | { 12 | /// 13 | /// Indicates success or failure. 14 | /// 15 | public bool Success { get; set; } 16 | 17 | /// 18 | /// The round trip time; nano second resolution. 19 | /// 20 | public TimeSpan Time { get; set; } 21 | 22 | /// 23 | /// The text to echo. 24 | /// 25 | public string Text { get; set; } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /src/Base32z.cs: -------------------------------------------------------------------------------- 1 | using SimpleBase; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Ipfs 7 | { 8 | /// 9 | /// Base32 encoding designed to be easier for human use and more compact. 10 | /// 11 | /// 12 | /// Commonly referred to as 'z-base-32'. 13 | /// 14 | /// 15 | public static class Base32z 16 | { 17 | static readonly Base32Alphabet alphabet = 18 | new Base32Alphabet("ybndrfg8ejkmcpqxot1uwisza345h769"); 19 | 20 | /// 21 | /// The encoder/decoder for z-base-32. 22 | /// 23 | public static readonly SimpleBase.Base32 Codec = new SimpleBase.Base32(alphabet); 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /src/CoreApi/BandwidthData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs.CoreApi 6 | { 7 | /// 8 | /// The statistics for . 9 | /// 10 | public class BandwidthData 11 | { 12 | /// 13 | /// The number of bytes received. 14 | /// 15 | public ulong TotalIn; 16 | 17 | /// 18 | /// The number of bytes sent. 19 | /// 20 | public ulong TotalOut; 21 | 22 | /// 23 | /// TODO 24 | /// 25 | public double RateIn; 26 | 27 | /// 28 | /// TODO 29 | /// 30 | public double RateOut; 31 | 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /src/IKey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs 6 | { 7 | /// 8 | /// Information about a cryptographic key. 9 | /// 10 | public interface IKey 11 | { 12 | /// 13 | /// Unique identifier. 14 | /// 15 | /// 16 | /// The of the key's public key. 17 | /// 18 | MultiHash Id { get; } 19 | 20 | /// 21 | /// The locally assigned name to the key. 22 | /// 23 | /// 24 | /// The name is only unique within the local peer node. The 25 | /// is universally unique. 26 | /// 27 | string Name { get; } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /test/CoreApi/ObjectStatTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Ipfs.CoreApi 7 | { 8 | [TestClass] 9 | public class ObjectStatTest 10 | { 11 | [TestMethod] 12 | public void Properties() 13 | { 14 | var stat = new ObjectStat 15 | { 16 | BlockSize = 1, 17 | CumulativeSize = 2, 18 | DataSize = 3, 19 | LinkCount = 4, 20 | LinkSize = 5 21 | }; 22 | Assert.AreEqual(1, stat.BlockSize); 23 | Assert.AreEqual(2, stat.CumulativeSize); 24 | Assert.AreEqual(3, stat.DataSize); 25 | Assert.AreEqual(4, stat.LinkCount); 26 | Assert.AreEqual(5, stat.LinkSize); 27 | } 28 | 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /src/Cryptography/DoubleSha256.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | 6 | namespace Ipfs.Cryptography 7 | { 8 | class DoubleSha256 : HashAlgorithm 9 | { 10 | HashAlgorithm digest = SHA256.Create(); 11 | byte[] round1; 12 | 13 | public override void Initialize() 14 | { 15 | digest.Initialize(); 16 | round1 = null; 17 | } 18 | 19 | public override int HashSize => digest.HashSize; 20 | 21 | protected override void HashCore(byte[] array, int ibStart, int cbSize) 22 | { 23 | if (round1 != null) 24 | throw new NotSupportedException("Already called."); 25 | 26 | round1 = digest.ComputeHash(array, ibStart, cbSize); 27 | } 28 | 29 | protected override byte[] HashFinal() 30 | { 31 | digest.Initialize(); 32 | return digest.ComputeHash(round1); 33 | } 34 | 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /src/IMerkleLink.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace Ipfs 3 | { 4 | 5 | /// 6 | /// A link to another node in IPFS. 7 | /// 8 | public interface IMerkleLink 9 | { 10 | 11 | /// 12 | /// A name associated with the linked node. 13 | /// 14 | /// A or null. 15 | /// 16 | /// 17 | /// IPFS considers a null name different from a 18 | /// name; 19 | /// 20 | /// 21 | string Name { get; } 22 | 23 | /// 24 | /// The unique ID of the link. 25 | /// 26 | /// 27 | /// A of the content. 28 | /// 29 | Cid Id { get; } 30 | 31 | /// 32 | /// The serialised size (in bytes) of the linked node. 33 | /// 34 | /// Number of bytes. 35 | long Size { get; } 36 | 37 | } 38 | } -------------------------------------------------------------------------------- /src/CoreApi/ObjectStat.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs.CoreApi 6 | { 7 | /// 8 | /// Information on a DAG node. 9 | /// 10 | /// 11 | public class ObjectStat 12 | { 13 | /// 14 | /// Number of links. 15 | /// 16 | public int LinkCount { get; set; } 17 | 18 | /// 19 | /// Size of the links segment. 20 | /// 21 | public long LinkSize { get; set; } 22 | 23 | /// 24 | /// Size of the raw, encoded data. 25 | /// 26 | public long BlockSize { get; set; } 27 | 28 | /// 29 | /// Siz of the data segment. 30 | /// 31 | public long DataSize { get; set; } 32 | 33 | /// 34 | /// Size of object and its references 35 | /// 36 | public long CumulativeSize { get; set; } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/Cryptography/IdentityHash.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Security.Cryptography; 4 | using System.Text; 5 | 6 | namespace Ipfs.Cryptography 7 | { 8 | class IdentityHash : HashAlgorithm 9 | { 10 | byte[] digest; 11 | 12 | public override void Initialize() 13 | { 14 | } 15 | 16 | protected override void HashCore(byte[] array, int ibStart, int cbSize) 17 | { 18 | if (digest == null) 19 | { 20 | digest = new byte[cbSize]; 21 | Buffer.BlockCopy(array, ibStart, digest, 0, cbSize); 22 | return; 23 | } 24 | 25 | var buffer = new byte[digest.Length + cbSize]; 26 | Buffer.BlockCopy(digest, 0, buffer, digest.Length, digest.Length); 27 | Buffer.BlockCopy(array, ibStart, digest, digest.Length, cbSize); 28 | digest = buffer; 29 | } 30 | 31 | protected override byte[] HashFinal() 32 | { 33 | return digest; 34 | } 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/IpfsCoreTests.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | net45;netcoreapp1.1;netcoreapp2.1;netcoreapp3.0 5 | 6 | false 7 | portable 8 | Ipfs 9 | 10 | 11 | 12 | 13 | 14 | false 15 | opencover 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/ProtobufHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Reflection; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Google.Protobuf; 8 | 9 | namespace Ipfs 10 | { 11 | static class ProtobufHelper 12 | { 13 | static MethodInfo writeRawBytes = typeof(CodedOutputStream) 14 | .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) 15 | .Single(m => 16 | m.Name == "WriteRawBytes" && m.GetParameters().Count() == 1 17 | ); 18 | static MethodInfo readRawBytes = typeof(CodedInputStream) 19 | .GetMethods(BindingFlags.NonPublic | BindingFlags.Instance) 20 | .Single(m => 21 | m.Name == "ReadRawBytes" 22 | ); 23 | 24 | public static void WriteSomeBytes(this CodedOutputStream stream, byte[] bytes) 25 | { 26 | writeRawBytes.Invoke(stream, new object[] { bytes }); 27 | } 28 | 29 | public static byte[] ReadSomeBytes(this CodedInputStream stream, int length) 30 | { 31 | return (byte[])readRawBytes.Invoke(stream, new object[] { length }); 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/CoreApi/IPeerRouting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading; 3 | using System.Threading.Tasks; 4 | 5 | namespace Ipfs.CoreApi 6 | { 7 | /// 8 | /// Find information about a peer. 9 | /// 10 | /// 11 | /// No IPFS documentation is currently available. See the 12 | /// code. 13 | /// 14 | public interface IPeerRouting 15 | { 16 | /// 17 | /// Information about an IPFS peer. 18 | /// 19 | /// 20 | /// The ID of the IPFS peer. 21 | /// 22 | /// 23 | /// Is used to stop the task. When cancelled, the is NOT raised. 24 | /// 25 | /// 26 | /// A task that represents the asynchronous operation that returns 27 | /// the information or a closer peer. 28 | /// 29 | Task FindPeerAsync(MultiHash id, CancellationToken cancel = default(CancellationToken)); 30 | 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /src/IPublishedMessage.cs: -------------------------------------------------------------------------------- 1 | using Ipfs.CoreApi; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | 6 | namespace Ipfs 7 | { 8 | /// 9 | /// A published message. 10 | /// 11 | /// 12 | /// The is used to publish and subsribe to a message. 13 | /// 14 | public interface IPublishedMessage : IDataBlock 15 | { 16 | /// 17 | /// The sender of the message. 18 | /// 19 | /// 20 | /// The peer that sent the message. 21 | /// 22 | Peer Sender { get; } 23 | 24 | /// 25 | /// The topics of the message. 26 | /// 27 | /// 28 | /// All topics related to this message. 29 | /// 30 | IEnumerable Topics { get; } 31 | 32 | /// 33 | /// The sequence number of the message. 34 | /// 35 | /// 36 | /// A sender unique id for the message. 37 | /// 38 | byte[] SequenceNumber { get; } 39 | 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /doc/articles/dag.md: -------------------------------------------------------------------------------- 1 | # Merkle DAG 2 | 3 | The [DagNode](xref:Ipfs.DagNode) is a directed acyclic graph whose edges are a 4 | [DagLink](xref:Ipfs.DagLink). This means that links to objects can authenticate 5 | the objects themselves, and that every object contains a secure 6 | representation of its children. 7 | 8 | Every Merkle node is a directed acyclic graph (DAG) because each child node is accessed via its [CID](cid.md), 9 | basically the [hash](multihash.md) of the child's dag node. It's [ID](xref:Ipfs.DagNode.Id) 10 | is the hash of its local content ([data](xref:Ipfs.DagNode.DataBytes) and [links](xref:Ipfs.DagNode.Links)). 11 | So after creation there is no way to edit a DagNode. This prevents cycles (assuming there are no hash collisions) 12 | since one can not link the first created node to the last node to create the last reference. 13 | 14 | ```csharp 15 | var a = Encoding.UTF8.GetBytes("a"); 16 | var anode = new DagNode(a); 17 | var alink = anode.ToLink("a"); 18 | 19 | var b = Encoding.UTF8.GetBytes("b"); 20 | var bnode = new DagNode(b); 21 | var blink = bnode.ToLink("b"); 22 | 23 | var node = new DagNode(null, new[] { alink, blink }); 24 | Assert.AreEqual(2, node.Links.Count()); 25 | Assert.AreEqual("QmbNgNPPykP4YTuAeSa3DsnBJWLVxccrqLUZDPNQfizGKs", (string)node.Id); 26 | ``` 27 | -------------------------------------------------------------------------------- /test/CoreApi/BitswapLedgerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Ipfs.CoreApi 7 | { 8 | [TestClass] 9 | public class BitswapLedgerTest 10 | { 11 | [TestMethod] 12 | public void Defaults() 13 | { 14 | var ledger = new BitswapLedger(); 15 | Assert.IsNull(ledger.Peer); 16 | Assert.AreEqual(0ul, ledger.BlocksExchanged); 17 | Assert.AreEqual(0ul, ledger.DataReceived); 18 | Assert.AreEqual(0ul, ledger.DataSent); 19 | Assert.AreEqual(0f, ledger.DebtRatio); 20 | Assert.IsTrue(ledger.IsInDebt); 21 | } 22 | 23 | [TestMethod] 24 | public void DebtRatio_Positive() 25 | { 26 | var ledger = new BitswapLedger { DataSent = 1024 * 1024 }; 27 | Assert.IsTrue(ledger.DebtRatio >= 1); 28 | Assert.IsFalse(ledger.IsInDebt); 29 | } 30 | 31 | [TestMethod] 32 | public void DebtRatio_Negative() 33 | { 34 | var ledger = new BitswapLedger { DataReceived = 1024 * 1024 }; 35 | Assert.IsTrue(ledger.DebtRatio < 1); 36 | Assert.IsTrue(ledger.IsInDebt); 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /src/Cryptography/BouncyDigest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs.Cryptography 6 | { 7 | /// 8 | /// Thin wrapper around bouncy castle digests. 9 | /// 10 | /// 11 | /// Makes a Bouncy Caslte IDigest speak .Net HashAlgorithm. 12 | /// 13 | internal class BouncyDigest : System.Security.Cryptography.HashAlgorithm 14 | { 15 | Org.BouncyCastle.Crypto.IDigest digest; 16 | 17 | /// 18 | /// Wrap the bouncy castle digest. 19 | /// 20 | public BouncyDigest(Org.BouncyCastle.Crypto.IDigest digest) 21 | { 22 | this.digest = digest; 23 | } 24 | 25 | /// 26 | public override void Initialize() 27 | { 28 | digest.Reset(); 29 | } 30 | 31 | /// 32 | protected override void HashCore(byte[] array, int ibStart, int cbSize) 33 | { 34 | digest.BlockUpdate(array, ibStart, cbSize); 35 | } 36 | 37 | /// 38 | protected override byte[] HashFinal() 39 | { 40 | var output = new byte[digest.GetDigestSize()]; 41 | digest.DoFinal(output, 0); 42 | return output; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /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/articles/core-api.md: -------------------------------------------------------------------------------- 1 | # Core API 2 | 3 | [ICoreApi](xref:Ipfs.CoreApi.ICoreApi) is a set of interfaces to IPFS features. 4 | 5 | 6 | | Feature | Purpose | 7 | | ------- | ------- | 8 | | [Bitswap](xref:Ipfs.CoreApi.IBitswapApi) | Block trading between peers | 9 | | [Block](xref:Ipfs.CoreApi.IBlockApi) | Manages the blocks | 10 | | [BlockRepository](xref:Ipfs.CoreApi.IBlockRepositoryApi) | Manages the repository for [blocks](xref:Ipfs.CoreApi.IBlockApi) | 11 | | [Bootstrap](xref:Ipfs.CoreApi.IBootstrapApi) | Trusted peers | 12 | | [Config](xref:Ipfs.CoreApi.IConfigApi) | Manages the configuration of the local peer | 13 | | [Dag](xref:Ipfs.CoreApi.IDagApi) | Manages the IPLD (linked data) Directed Acrylic Graph | 14 | | [Dht](xref:Ipfs.CoreApi.IDhtApi) | Manages the Distributed Hash Table | 15 | | [Dns](xref:Ipfs.CoreApi.IDnsApi) | DNS mapping to IPFS | 16 | | [FileSystem](xref:Ipfs.CoreApi.IFileSystemApi) | Manages the files/directories in IPFS | 17 | | [Key](xref:Ipfs.CoreApi.IKeyApi) | Manages the cryptographic keys | 18 | | [Misc](xref:Ipfs.CoreApi.IGenericApi) | Some miscellaneous methods | 19 | | [Name](xref:Ipfs.CoreApi.INameApi) | Manages the Interplanetary Name Space (IPNS) | 20 | | [Object](xref:Ipfs.CoreApi.IObjectApi) | Manages the IPFS Directed Acrylic Graph | 21 | | [Pin](xref:Ipfs.CoreApi.IPinApi) | Manage objects that are locally stored and permanent | 22 | | [PubSub](xref:Ipfs.CoreApi.IPubSubApi) | Publish and subscribe topic messages | 23 | | [Swarm](xref:Ipfs.CoreApi.ISwarmApi) | Manages the swarm of peers | 24 | | [Stats](xref:Ipfs.CoreApi.IStatsApi) | Statistics on IPFS components | 25 | 26 | -------------------------------------------------------------------------------- /test/ExceptionAssert.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Ipfs 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/Registry/CodecTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.IO; 7 | using Google.Protobuf; 8 | 9 | namespace Ipfs.Registry 10 | { 11 | [TestClass] 12 | public class CodecTest 13 | { 14 | [TestMethod] 15 | public void Bad_Name() 16 | { 17 | ExceptionAssert.Throws(() => Codec.Register(null, 1)); 18 | ExceptionAssert.Throws(() => Codec.Register("", 1)); 19 | ExceptionAssert.Throws(() => Codec.Register(" ", 1)); 20 | } 21 | 22 | [TestMethod] 23 | public void Name_Already_Exists() 24 | { 25 | ExceptionAssert.Throws(() => Codec.Register("raw", 1)); 26 | } 27 | 28 | [TestMethod] 29 | public void Code_Already_Exists() 30 | { 31 | ExceptionAssert.Throws(() => Codec.Register("raw-x", 0x55)); 32 | } 33 | 34 | [TestMethod] 35 | public void Algorithms_Are_Enumerable() 36 | { 37 | Assert.AreNotEqual(0, Codec.All.Count()); 38 | } 39 | 40 | [TestMethod] 41 | public void Register() 42 | { 43 | var codec = Codec.Register("something-new", 0x0bad); 44 | try 45 | { 46 | Assert.AreEqual("something-new", codec.Name); 47 | Assert.AreEqual("something-new", codec.ToString()); 48 | Assert.AreEqual(0x0bad, codec.Code); 49 | } 50 | finally 51 | { 52 | Codec.Deregister(codec); 53 | } 54 | } 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/CoreApi/RepositoryData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs.CoreApi 6 | { 7 | /// 8 | /// The statistics for . 9 | /// 10 | public class RepositoryData 11 | { 12 | /// 13 | /// The number of blocks in the repository. 14 | /// 15 | /// 16 | /// The number of blocks in the repository. 17 | /// 18 | public ulong NumObjects; 19 | 20 | /// 21 | /// The total number bytes in the repository. 22 | /// 23 | /// 24 | /// The total number bytes in the repository. 25 | /// 26 | public ulong RepoSize; 27 | 28 | /// 29 | /// The fully qualified path to the repository. 30 | /// 31 | /// 32 | /// The directory of the repository. 33 | /// 34 | public string RepoPath; 35 | 36 | /// 37 | /// The version number of the repository. 38 | /// 39 | /// 40 | /// The version number of the repository. 41 | /// 42 | public string Version; 43 | 44 | /// 45 | /// The maximum number of bytes allowed in the repository. 46 | /// 47 | /// 48 | /// Max bytes allowed in the repository. 49 | /// 50 | public ulong StorageMax; 51 | 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/IDataBlock.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs 10 | { 11 | /// 12 | /// Some data that is stored in IPFS. 13 | /// 14 | /// 15 | /// A DataBlock has an unique ID 16 | /// and some data ( 17 | /// or ). 18 | /// 19 | /// It is useful to talk about them as "blocks" in Bitswap 20 | /// and other things that do not care about what is being stored. 21 | /// 22 | /// 23 | /// 24 | public interface IDataBlock 25 | { 26 | 27 | /// 28 | /// Contents as a byte array. 29 | /// 30 | /// 31 | /// It is never null. 32 | /// 33 | /// 34 | /// The contents as a sequence of bytes. 35 | /// 36 | byte[] DataBytes { get; } 37 | 38 | /// 39 | /// Contents as a stream of bytes. 40 | /// 41 | /// 42 | /// The contents as a stream of bytes. 43 | /// 44 | Stream DataStream { get; } 45 | 46 | /// 47 | /// The unique ID of the data. 48 | /// 49 | /// 50 | /// A of the content. 51 | /// 52 | Cid Id { get; } 53 | 54 | /// 55 | /// The size (in bytes) of the data. 56 | /// 57 | /// Number of bytes. 58 | long Size { get; } 59 | 60 | 61 | } 62 | 63 | } 64 | -------------------------------------------------------------------------------- /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": "netcoreapp3.0" 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 | "globalMetadata": { 71 | "_appTitle": "IPFS Core documentation", 72 | "_appFooter": "Generated by DocFX", 73 | "_appFaviconPath": "images/ipfs-favicon.ico", 74 | "_appLogoPath": "images/ipfs-cs-logo-48x48.png" 75 | }, 76 | "dest": "_site", 77 | "globalMetadataFiles": [], 78 | "fileMetadataFiles": [], 79 | "template": [ 80 | "default" 81 | ], 82 | "postProcessors": [], 83 | "noLangKeyword": false 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /test/NetworkProtocolTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.IO; 8 | using Google.Protobuf; 9 | 10 | namespace Ipfs 11 | { 12 | [TestClass] 13 | public class NetworkProtocolTest 14 | { 15 | [TestMethod] 16 | public void Stringing() 17 | { 18 | Assert.AreEqual("/tcp/8080", new MultiAddress("/tcp/8080").Protocols[0].ToString()); 19 | } 20 | 21 | [TestMethod] 22 | public void Register_Name_Already_Exists() 23 | { 24 | ExceptionAssert.Throws(() => NetworkProtocol.Register()); 25 | } 26 | 27 | [TestMethod] 28 | public void Register_Code_Already_Exists() 29 | { 30 | ExceptionAssert.Throws(() => NetworkProtocol.Register()); 31 | } 32 | 33 | class NameExists : NetworkProtocol 34 | { 35 | public override string Name { get { return "tcp"; } } 36 | public override uint Code { get { return 0x7FFF; } } 37 | public override void ReadValue(CodedInputStream stream) { } 38 | public override void ReadValue(TextReader stream) { } 39 | public override void WriteValue(CodedOutputStream stream) { } 40 | } 41 | 42 | class CodeExists : NetworkProtocol 43 | { 44 | public override string Name { get { return "x-tcp"; } } 45 | public override uint Code { get { return 6; } } 46 | public override void ReadValue(CodedInputStream stream) { } 47 | public override void ReadValue(TextReader stream) { } 48 | public override void WriteValue(CodedOutputStream stream) { } 49 | } 50 | 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /src/IMerkleNode.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs 10 | { 11 | /// 12 | /// A Directed Acyclic Graph (DAG) in IPFS. 13 | /// 14 | /// 15 | /// A MerkleNode has a sequence of navigable 16 | /// and some data ( 17 | /// or ). 18 | /// 19 | /// 20 | /// The type of used by this node. 21 | /// 22 | /// 23 | /// 24 | public interface IMerkleNode : IDataBlock 25 | where Link : IMerkleLink 26 | { 27 | 28 | /// 29 | /// Links to other nodes. 30 | /// 31 | /// 32 | /// A sequence of . 33 | /// 34 | /// 35 | /// It is never null. 36 | /// 37 | /// The links are sorted ascending by . A null 38 | /// name is compared as "". 39 | /// 40 | /// 41 | IEnumerable Links { get; } 42 | 43 | /// 44 | /// Returns a link to the node. 45 | /// 46 | /// 47 | /// A for the link; defaults to "". 48 | /// 49 | /// 50 | /// A new to the node. 51 | /// 52 | Link ToLink(string name = ""); 53 | 54 | } 55 | 56 | } 57 | -------------------------------------------------------------------------------- /test/Base58Test.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | 7 | namespace Ipfs 8 | { 9 | [TestClass] 10 | public class Base58Test 11 | { 12 | [TestMethod] 13 | public void Encode() 14 | { 15 | Assert.AreEqual("jo91waLQA1NNeBmZKUF", Base58.Encode(Encoding.UTF8.GetBytes("this is a test"))); 16 | Assert.AreEqual("jo91waLQA1NNeBmZKUF", Encoding.UTF8.GetBytes("this is a test").ToBase58()); 17 | } 18 | 19 | [TestMethod] 20 | public void Decode() 21 | { 22 | Assert.AreEqual("this is a test", Encoding.UTF8.GetString(Base58.Decode("jo91waLQA1NNeBmZKUF"))); 23 | Assert.AreEqual("this is a test", Encoding.UTF8.GetString("jo91waLQA1NNeBmZKUF".FromBase58())); 24 | } 25 | 26 | /// 27 | /// C# version of base58Test in 28 | /// 29 | [TestMethod] 30 | public void Java() 31 | { 32 | String input = "QmPZ9gcCEpqKTo6aq61g2nXGUhM4iCL3ewB6LDXZCtioEB"; 33 | byte[] output = Base58.Decode(input); 34 | String encoded = Base58.Encode(output); 35 | Assert.AreEqual(input, encoded); 36 | } 37 | 38 | [TestMethod] 39 | public void Decode_Bad() 40 | { 41 | ExceptionAssert.Throws(() => Base58.Decode("jo91waLQA1NNeBmZKUF==")); 42 | } 43 | 44 | [TestMethod] 45 | public void Zero() 46 | { 47 | Assert.AreEqual("1111111", Base58.Encode(new byte[7])); 48 | Assert.AreEqual(7, Base58.Decode("1111111").Length); 49 | Assert.IsTrue(Base58.Decode("1111111").All(b => b == 0)); 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /test/CoreApi/AddFileOptionsTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Linq; 3 | using System.Text; 4 | using Microsoft.VisualStudio.TestTools.UnitTesting; 5 | 6 | namespace Ipfs.CoreApi 7 | { 8 | [TestClass] 9 | public class AddFileOptionsTests 10 | { 11 | [TestMethod] 12 | public void Defaults() 13 | { 14 | var options = new AddFileOptions(); 15 | 16 | Assert.AreEqual(true, options.Pin); 17 | Assert.AreEqual(256 * 1024, options.ChunkSize); 18 | Assert.AreEqual(MultiHash.DefaultAlgorithmName, options.Hash); 19 | Assert.AreEqual(false, options.OnlyHash); 20 | Assert.AreEqual(false, options.RawLeaves); 21 | Assert.AreEqual(false, options.Trickle); 22 | Assert.AreEqual(false, options.Wrap); 23 | Assert.IsNull(options.Progress); 24 | Assert.IsNull(options.ProtectionKey); 25 | } 26 | 27 | [TestMethod] 28 | public void Setting() 29 | { 30 | var options = new AddFileOptions 31 | { 32 | Pin = false, 33 | ChunkSize = 2 * 1024, 34 | Hash = "sha2-512", 35 | OnlyHash = true, 36 | RawLeaves = true, 37 | Progress = new Progress(), 38 | Trickle = true, 39 | Wrap = true, 40 | ProtectionKey = "secret" 41 | }; 42 | 43 | Assert.AreEqual(false, options.Pin); 44 | Assert.AreEqual(2 * 1024, options.ChunkSize); 45 | Assert.AreEqual("sha2-512", options.Hash); 46 | Assert.AreEqual(true, options.OnlyHash); 47 | Assert.AreEqual(true, options.RawLeaves); 48 | Assert.AreEqual(true, options.Trickle); 49 | Assert.AreEqual(true, options.Wrap); 50 | Assert.IsNotNull(options.Progress); 51 | Assert.AreEqual("secret", options.ProtectionKey); 52 | } 53 | 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/CoreApi/BitswapData.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs.CoreApi 6 | { 7 | /// 8 | /// The statistics for . 9 | /// 10 | public class BitswapData 11 | { 12 | /// 13 | /// TODO: Unknown. 14 | /// 15 | public int ProvideBufLen; 16 | 17 | /// 18 | /// The content that is wanted. 19 | /// 20 | public IEnumerable Wantlist; 21 | 22 | /// 23 | /// The known peers. 24 | /// 25 | public IEnumerable Peers; 26 | 27 | /// 28 | /// The number of blocks sent by other peers. 29 | /// 30 | public ulong BlocksReceived; 31 | 32 | /// 33 | /// The number of bytes sent by other peers. 34 | /// 35 | public ulong DataReceived; 36 | 37 | /// 38 | /// The number of blocks sent to other peers. 39 | /// 40 | public ulong BlocksSent; 41 | 42 | /// 43 | /// The number of bytes sent to other peers. 44 | /// 45 | public ulong DataSent; 46 | 47 | /// 48 | /// The number of duplicate blocks sent by other peers. 49 | /// 50 | /// 51 | /// A duplicate block is a block that is already stored in the 52 | /// local repository. 53 | /// 54 | public ulong DupBlksReceived; 55 | 56 | /// 57 | /// The number of duplicate bytes sent by other peers. 58 | /// 59 | /// 60 | /// A duplicate block is a block that is already stored in the 61 | /// local repository. 62 | /// 63 | public ulong DupDataReceived; 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /test/HexStringTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using System.IO; 7 | 8 | namespace Ipfs 9 | { 10 | [TestClass] 11 | public class HexStringTest 12 | { 13 | [TestMethod] 14 | public void Encode() 15 | { 16 | var buffer = Enumerable.Range(byte.MinValue, byte.MaxValue).Select(b => (byte) b).ToArray(); 17 | var lowerHex = string.Concat(buffer.Select(b => b.ToString("x2")).ToArray()); 18 | var upperHex = string.Concat(buffer.Select(b => b.ToString("X2")).ToArray()); 19 | 20 | Assert.AreEqual(lowerHex, buffer.ToHexString(), "encode default"); 21 | Assert.AreEqual(lowerHex, buffer.ToHexString("G"), "encode general"); 22 | Assert.AreEqual(lowerHex, buffer.ToHexString("x"), "encode lower"); 23 | Assert.AreEqual(upperHex, buffer.ToHexString("X"), "encode upper"); 24 | } 25 | 26 | [TestMethod] 27 | public void Decode() 28 | { 29 | var buffer = Enumerable.Range(byte.MinValue, byte.MaxValue).Select(b => (byte)b).ToArray(); 30 | var lowerHex = string.Concat(buffer.Select(b => b.ToString("x2")).ToArray()); 31 | var upperHex = string.Concat(buffer.Select(b => b.ToString("X2")).ToArray()); 32 | 33 | CollectionAssert.AreEqual(buffer, lowerHex.ToHexBuffer(), "decode lower"); 34 | CollectionAssert.AreEqual(buffer, upperHex.ToHexBuffer(), "decode upper"); 35 | } 36 | 37 | [TestMethod] 38 | public void InvalidFormatSpecifier() 39 | { 40 | ExceptionAssert.Throws(() => HexString.Encode(new byte[0], "...")); 41 | } 42 | 43 | [TestMethod] 44 | public void InvalidHexStrings() 45 | { 46 | ExceptionAssert.Throws(() => HexString.Decode("0")); 47 | ExceptionAssert.Throws(() => HexString.Decode("0Z")); 48 | } 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /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 | -------------------------------------------------------------------------------- /src/CoreApi/IDnsApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.CoreApi 8 | { 9 | /// 10 | /// DNS mapping to IPFS. 11 | /// 12 | /// 13 | /// Multihashes are hard to remember, but domain names are usually easy to 14 | /// remember. To create memorable aliases for multihashes, DNS TXT 15 | /// records can point to other DNS links, IPFS objects, IPNS keys, etc. 16 | /// 17 | public interface IDnsApi 18 | { 19 | 20 | /// 21 | /// Resolve a domain name to an IPFS path. 22 | /// 23 | /// 24 | /// An domain name, such as "ipfs.io". 25 | /// 26 | /// 27 | /// Resolve until the result is not a DNS link. Defaults to false. 28 | /// 29 | /// 30 | /// Is used to stop the task. When cancelled, the is raised. 31 | /// 32 | /// 33 | /// A task that represents the asynchronous operation. The task's value is 34 | /// the resolved IPFS path as a , such as 35 | /// /ipfs/QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao. 36 | /// 37 | /// 38 | /// A DNS TXT record with a "dnslink=..." entry is expected to exist. The 39 | /// value of the "dnslink" is an IPFS path to another IPFS object. 40 | /// 41 | /// A DNS query is generated for both and 42 | /// _dnslink.. 43 | /// 44 | /// 45 | /// 46 | /// ResolveAsync("ipfs.io", recursive: false) produces "/ipns/website.ipfs.io". Whereas, 47 | /// ResolveAsync("ipfs.io", recursive: true) produces "/ipfs/QmXZz6vQTMiu6UyGxVgpLB6xJdHvvUbhdWagJQNnxXAjpn". 48 | /// 49 | Task ResolveAsync( 50 | string name, 51 | bool recursive = false, 52 | CancellationToken cancel = default(CancellationToken) 53 | ); 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /src/CoreApi/IStatsApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.CoreApi 8 | { 9 | /// 10 | /// Get statistics/diagnostics for the various core components. 11 | /// 12 | public interface IStatsApi 13 | { 14 | /// 15 | /// Get statistics on network bandwidth. 16 | /// 17 | /// 18 | /// Is used to stop the task. When cancelled, the is raised. 19 | /// 20 | /// 21 | /// A task that represents the asynchronous operation. The task's result is 22 | /// the current . 23 | /// 24 | /// 25 | Task BandwidthAsync(CancellationToken cancel = default(CancellationToken)); 26 | 27 | /// 28 | /// Get statistics on the blocks exchanged with other peers. 29 | /// 30 | /// 31 | /// Is used to stop the task. When cancelled, the is raised. 32 | /// 33 | /// 34 | /// A task that represents the asynchronous operation. The task's result is 35 | /// the current . 36 | /// 37 | /// 38 | Task BitswapAsync(CancellationToken cancel = default(CancellationToken)); 39 | 40 | /// 41 | /// Get statistics on the repository. 42 | /// 43 | /// 44 | /// Is used to stop the task. When cancelled, the is raised. 45 | /// 46 | /// 47 | /// A task that represents the asynchronous operation. The task's result is 48 | /// the current . 49 | /// 50 | /// 51 | /// Same as . 52 | /// 53 | /// 54 | Task RepositoryAsync(CancellationToken cancel = default(CancellationToken)); 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /src/CoreApi/BitswapLedger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs.CoreApi 6 | { 7 | /// 8 | /// Statistics on the bitswap blocks exchanged with another . 9 | /// 10 | /// 11 | public class BitswapLedger 12 | { 13 | /// 14 | /// The that pertains to this ledger. 15 | /// 16 | /// 17 | /// The peer that is being monitored. 18 | /// 19 | public Peer Peer { get; set; } 20 | 21 | /// 22 | /// The number of blocks exchanged with the . 23 | /// 24 | /// 25 | /// The number of blocks sent by the peer or sent by us to the peer. 26 | /// 27 | public ulong BlocksExchanged { get; set; } 28 | 29 | /// 30 | /// The number of bytes sent by the to us. 31 | /// 32 | /// 33 | /// The number of bytes. 34 | /// 35 | public ulong DataReceived { get; set; } 36 | 37 | /// 38 | /// The number of bytes sent by us to the 39 | /// 40 | /// 41 | /// The number of bytes. 42 | /// 43 | public ulong DataSent { get; set; } 44 | 45 | /// 46 | /// The calculated debt to the peer. 47 | /// 48 | /// 49 | /// divided by . 50 | /// A value less than 1 indicates that we are in debt to the 51 | /// . 52 | /// 53 | public float DebtRatio 54 | { 55 | get 56 | { 57 | return (float)DataSent / (float)(DataReceived + 1); // +1 is to prevent division by zero 58 | } 59 | } 60 | 61 | /// 62 | /// Determines if we owe the some blocks. 63 | /// 64 | /// 65 | /// true if we owe data to the peer; otherwise, false. 66 | /// 67 | public bool IsInDebt => DebtRatio < 1; 68 | 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /src/CoreApi/IContentRouting.cs: -------------------------------------------------------------------------------- 1 | using Ipfs; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs.CoreApi 10 | { 11 | /// 12 | /// Find information about who has what content. 13 | /// 14 | /// 15 | /// No IPFS documentation is currently available. See the 16 | /// code. 17 | /// 18 | public interface IContentRouting 19 | { 20 | /// 21 | /// Adds the to the content routing system. 22 | /// 23 | /// 24 | /// The ID of some content that the peer contains. 25 | /// 26 | /// 27 | /// Advertise the to other peers. 28 | /// 29 | /// 30 | /// Is used to stop the task. When cancelled, the is raised. 31 | /// 32 | /// 33 | /// A task that represents the asynchronous operation. 34 | /// 35 | Task ProvideAsync(Cid cid, bool advertise = true, CancellationToken cancel = default(CancellationToken)); 36 | 37 | /// 38 | /// Find the providers for the specified content. 39 | /// 40 | /// 41 | /// The of the content. 42 | /// 43 | /// 44 | /// The maximum number of peers to return. Defaults to 20. 45 | /// 46 | /// 47 | /// An action to perform when a provider is found. 48 | /// 49 | /// 50 | /// Is used to stop the task. When cancelled, the is raised. 51 | /// 52 | /// 53 | /// A task that represents the asynchronous operation that returns 54 | /// a sequence of IPFS . 55 | /// 56 | Task> FindProvidersAsync( 57 | Cid id, 58 | int limit = 20, 59 | Action providerFound = null, 60 | CancellationToken cancel = default(CancellationToken)); 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /test/Registry/MultibaseAlgorithmTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.IO; 7 | using Google.Protobuf; 8 | 9 | namespace Ipfs.Registry 10 | { 11 | [TestClass] 12 | public class MultiBaseAlgorithmTest 13 | { 14 | [TestMethod] 15 | public void Bad_Name() 16 | { 17 | ExceptionAssert.Throws(() => MultiBaseAlgorithm.Register(null, '?')); 18 | ExceptionAssert.Throws(() => MultiBaseAlgorithm.Register("", '?')); 19 | ExceptionAssert.Throws(() => MultiBaseAlgorithm.Register(" ", '?')); 20 | } 21 | 22 | [TestMethod] 23 | public void Name_Already_Exists() 24 | { 25 | ExceptionAssert.Throws(() => MultiBaseAlgorithm.Register("base58btc", 'z')); 26 | } 27 | 28 | [TestMethod] 29 | public void Code_Already_Exists() 30 | { 31 | ExceptionAssert.Throws(() => MultiBaseAlgorithm.Register("base58btc-x", 'z')); 32 | } 33 | 34 | [TestMethod] 35 | public void Algorithms_Are_Enumerable() 36 | { 37 | Assert.AreNotEqual(0, MultiBaseAlgorithm.All.Count()); 38 | } 39 | 40 | [TestMethod] 41 | public void Roundtrip_All_Algorithms() 42 | { 43 | var bytes = new byte[] { 1, 2, 3, 4, 5 }; 44 | 45 | foreach (var alg in MultiBaseAlgorithm.All) 46 | { 47 | var s = alg.Encode(bytes); 48 | CollectionAssert.AreEqual(bytes, alg.Decode(s), alg.Name); 49 | } 50 | } 51 | 52 | [TestMethod] 53 | public void Name_Is_Also_ToString() 54 | { 55 | foreach (var alg in MultiBaseAlgorithm.All) 56 | { 57 | Assert.AreEqual(alg.Name, alg.ToString()); 58 | } 59 | } 60 | 61 | [TestMethod] 62 | public void Known_But_NYI() 63 | { 64 | var alg = MultiBaseAlgorithm.Register("nyi", 'n'); 65 | try 66 | { 67 | ExceptionAssert.Throws(() => alg.Encode(null)); 68 | ExceptionAssert.Throws(() => alg.Decode(null)); 69 | } 70 | finally 71 | { 72 | MultiBaseAlgorithm.Deregister(alg); 73 | } 74 | } 75 | } 76 | } 77 | -------------------------------------------------------------------------------- /src/CoreApi/IValueStore.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.CoreApi 8 | { 9 | /// 10 | /// A basic Put/Get interface. 11 | /// 12 | public interface IValueStore 13 | { 14 | /// 15 | /// Gets th value of a key. 16 | /// 17 | /// 18 | /// A byte array representing the name of a key. 19 | /// 20 | /// 21 | /// Is used to stop the task. When cancelled, the is raised. 22 | /// 23 | /// 24 | /// A task that represents the asynchronous operation that returns 25 | /// the value of the key as a byte array. 26 | /// 27 | Task GetAsync(byte[] key, CancellationToken cancel = default(CancellationToken)); 28 | 29 | /// 30 | /// Tries to get the value of a key. 31 | /// 32 | /// 33 | /// A byte array representing the name of a key. 34 | /// 35 | /// 36 | /// A byte array containing the value of the 37 | /// 38 | /// 39 | /// Is used to stop the task. When cancelled, the is raised. 40 | /// 41 | /// 42 | /// A task that represents the asynchronous operation that returns 43 | /// true if the key exists; otherwise, false. 44 | /// 45 | Task TryGetAsync(byte[] key, out byte[] value, CancellationToken cancel = default(CancellationToken)); 46 | 47 | /// 48 | /// Put the value of a key. 49 | /// 50 | /// 51 | /// A byte array representing the name of a key. 52 | /// 53 | /// 54 | /// A byte array containing the value of the 55 | /// 56 | /// 57 | /// Is used to stop the task. When cancelled, the is raised. 58 | /// 59 | /// 60 | /// A task that represents the asynchronous operation. 61 | /// 62 | Task PutAsync(byte[] key, out byte[] value, CancellationToken cancel = default(CancellationToken)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /IpfsCore.sln: -------------------------------------------------------------------------------- 1 | Microsoft Visual Studio Solution File, Format Version 12.00 2 | # Visual Studio 15 3 | VisualStudioVersion = 15.0.27004.2005 4 | MinimumVisualStudioVersion = 10.0.40219.1 5 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpfsCore", "src\IpfsCore.csproj", "{6401AF07-B7F2-4664-86AA-FAD99F82DFAD}" 6 | EndProject 7 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IpfsCoreTests", "test\IpfsCoreTests.csproj", "{FE639401-D4C7-4528-A07C-4290A7710889}" 8 | EndProject 9 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DA715302-8746-4884-95F9-B56F069A0451}" 10 | ProjectSection(SolutionItems) = preProject 11 | .codacy.yml = .codacy.yml 12 | .travis.yml = .travis.yml 13 | appveyor.yml = appveyor.yml 14 | builddocs.cmd = builddocs.cmd 15 | IpfsCore.vsmdi = IpfsCore.vsmdi 16 | LICENSE = LICENSE 17 | Local.testsettings = Local.testsettings 18 | README.md = README.md 19 | TraceAndTestImpact.testsettings = TraceAndTestImpact.testsettings 20 | EndProjectSection 21 | EndProject 22 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Documentation", "doc\Documentation.csproj", "{F3A32EA9-0B2F-46A3-B47A-33B4C04BD423}" 23 | EndProject 24 | Global 25 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 26 | Debug|Any CPU = Debug|Any CPU 27 | Release|Any CPU = Release|Any CPU 28 | EndGlobalSection 29 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 30 | {6401AF07-B7F2-4664-86AA-FAD99F82DFAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 31 | {6401AF07-B7F2-4664-86AA-FAD99F82DFAD}.Debug|Any CPU.Build.0 = Debug|Any CPU 32 | {6401AF07-B7F2-4664-86AA-FAD99F82DFAD}.Release|Any CPU.ActiveCfg = Release|Any CPU 33 | {6401AF07-B7F2-4664-86AA-FAD99F82DFAD}.Release|Any CPU.Build.0 = Release|Any CPU 34 | {FE639401-D4C7-4528-A07C-4290A7710889}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 35 | {FE639401-D4C7-4528-A07C-4290A7710889}.Debug|Any CPU.Build.0 = Debug|Any CPU 36 | {FE639401-D4C7-4528-A07C-4290A7710889}.Release|Any CPU.ActiveCfg = Release|Any CPU 37 | {FE639401-D4C7-4528-A07C-4290A7710889}.Release|Any CPU.Build.0 = Release|Any CPU 38 | {F3A32EA9-0B2F-46A3-B47A-33B4C04BD423}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 39 | {F3A32EA9-0B2F-46A3-B47A-33B4C04BD423}.Release|Any CPU.ActiveCfg = Release|Any CPU 40 | EndGlobalSection 41 | GlobalSection(SolutionProperties) = preSolution 42 | HideSolutionNode = FALSE 43 | EndGlobalSection 44 | GlobalSection(ExtensibilityGlobals) = postSolution 45 | SolutionGuid = {36ED5AA7-8F41-4F7D-A665-230635EF64A1} 46 | EndGlobalSection 47 | GlobalSection(TestCaseManagementSettings) = postSolution 48 | CategoryFile = IpfsCore.vsmdi 49 | EndGlobalSection 50 | EndGlobal 51 | -------------------------------------------------------------------------------- /src/CoreApi/IBlockRepository.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.CoreApi 8 | { 9 | /// 10 | /// Manages all the blocks stored locally. 11 | /// 12 | /// 13 | public interface IBlockRepositoryApi 14 | { 15 | /// 16 | /// Perform a garbage collection sweep on the repo. 17 | /// 18 | /// 19 | /// Is used to stop the task. When cancelled, the is raised. 20 | /// 21 | /// 22 | /// TODO: not sure what this should return. 23 | /// 24 | Task RemoveGarbageAsync(CancellationToken cancel = default(CancellationToken)); 25 | 26 | /// 27 | /// Get statistics on the repository. 28 | /// 29 | /// 30 | /// Is used to stop the task. When cancelled, the is raised. 31 | /// 32 | /// 33 | /// A task that represents the asynchronous operation. The task's result is 34 | /// the current . 35 | /// 36 | /// 37 | /// Same as . 38 | /// 39 | Task StatisticsAsync(CancellationToken cancel = default(CancellationToken)); 40 | 41 | /// 42 | /// Verify all blocks in repo are not corrupted. 43 | /// 44 | /// 45 | /// Is used to stop the task. When cancelled, the is raised. 46 | /// 47 | /// 48 | /// TODO: not sure what this should return. 49 | /// 50 | Task VerifyAsync(CancellationToken cancel = default(CancellationToken)); 51 | 52 | /// 53 | /// Gets the version number of the repo. 54 | /// 55 | /// 56 | /// Is used to stop the task. When cancelled, the is raised. 57 | /// 58 | /// 59 | /// A task that represents the asynchronous operation. The task's result is 60 | /// the version number of the data block repository. 61 | /// 62 | Task VersionAsync(CancellationToken cancel = default(CancellationToken)); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /test/Base32Test.cs: -------------------------------------------------------------------------------- 1 | using System.Linq; 2 | using System.Text; 3 | using Microsoft.VisualStudio.TestTools.UnitTesting; 4 | 5 | namespace Ipfs 6 | { 7 | [TestClass] 8 | public class Base32EncodeTests 9 | { 10 | byte[] GetStringBytes(string x) 11 | { 12 | return Encoding.ASCII.GetBytes(x); 13 | } 14 | 15 | [TestMethod] 16 | public void Vector1() 17 | { 18 | Assert.AreEqual(string.Empty, Base32.Encode(GetStringBytes(string.Empty))); 19 | } 20 | 21 | [TestMethod] 22 | public void Vector2() 23 | { 24 | Assert.AreEqual("my", Base32.Encode(GetStringBytes("f"))); 25 | } 26 | 27 | [TestMethod] 28 | public void Vector3() 29 | { 30 | Assert.AreEqual("mzxq", Base32.Encode(GetStringBytes("fo"))); 31 | } 32 | 33 | [TestMethod] 34 | public void Vector4() 35 | { 36 | Assert.AreEqual("mzxw6", Base32.Encode(GetStringBytes("foo"))); 37 | } 38 | 39 | [TestMethod] 40 | public void Vector5() 41 | { 42 | Assert.AreEqual("mzxw6yq", Base32.Encode(GetStringBytes("foob"))); 43 | } 44 | 45 | [TestMethod] 46 | public void Vector6() 47 | { 48 | Assert.AreEqual("mzxw6ytb", Base32.Encode(GetStringBytes("fooba"))); 49 | } 50 | 51 | [TestMethod] 52 | public void Vector7() 53 | { 54 | Assert.AreEqual("mzxw6ytboi", Base32.Encode(GetStringBytes("foobar"))); 55 | } 56 | } 57 | 58 | [TestClass] 59 | public class Base32DecodeTests 60 | { 61 | byte[] GetStringBytes(string x) 62 | { 63 | return Encoding.ASCII.GetBytes(x); 64 | } 65 | 66 | [TestMethod] 67 | public void Vector1() 68 | { 69 | CollectionAssert.AreEqual(GetStringBytes(string.Empty), Base32.Decode(string.Empty)); 70 | } 71 | 72 | [TestMethod] 73 | public void Vector2() 74 | { 75 | CollectionAssert.AreEqual(GetStringBytes("f"), Base32.Decode("MY======")); 76 | } 77 | 78 | [TestMethod] 79 | public void Vector3() 80 | { 81 | CollectionAssert.AreEqual(GetStringBytes("fo"), Base32.Decode("MZXQ====")); 82 | } 83 | 84 | [TestMethod] 85 | public void Vector4() 86 | { 87 | CollectionAssert.AreEqual(GetStringBytes("foo"), Base32.Decode("MZXW6===")); 88 | } 89 | 90 | [TestMethod] 91 | public void Vector5() 92 | { 93 | CollectionAssert.AreEqual(GetStringBytes("foob"), Base32.Decode("MZXW6YQ=")); 94 | } 95 | 96 | [TestMethod] 97 | public void Vector6() 98 | { 99 | CollectionAssert.AreEqual(GetStringBytes("fooba"), Base32.Decode("MZXW6YTB")); 100 | } 101 | 102 | [TestMethod] 103 | public void Vector7() 104 | { 105 | CollectionAssert.AreEqual(GetStringBytes("foobar"), Base32.Decode("MZXW6YTBOI======")); 106 | } 107 | } 108 | } 109 | -------------------------------------------------------------------------------- /src/CoreApi/IPinApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.CoreApi 8 | { 9 | /// 10 | /// Manage pinned objects (locally stored and permanent). 11 | /// 12 | /// Pin API spec 13 | public interface IPinApi 14 | { 15 | /// 16 | /// Adds an IPFS object to the pinset and also stores it to the IPFS repo. pinset is the set of hashes currently pinned (not gc'able). 17 | /// 18 | /// 19 | /// A CID or path to an existing object, such as "QmXarR6rgkQ2fDSHjSY5nM2kuCXKYGViky5nohtwgF65Ec/about" 20 | /// or "QmZTR5bcpQD7cFgTorqxZDYaew1Wqgfbd2ud9QqGPAkK2V" 21 | /// 22 | /// 23 | /// true to recursively pin links of the object; otherwise, false to only pin 24 | /// the specified object. Default is true. 25 | /// 26 | /// 27 | /// Is used to stop the task. When cancelled, the is raised. 28 | /// 29 | /// 30 | /// A task that represents the asynchronous operation. The task's value 31 | /// is a sequence of that were pinned. 32 | /// 33 | Task> AddAsync(string path, bool recursive = true, CancellationToken cancel = default(CancellationToken)); 34 | 35 | /// 36 | /// List all the objects pinned to local storage. 37 | /// 38 | /// 39 | /// Is used to stop the task. When cancelled, the is raised. 40 | /// 41 | /// 42 | /// A task that represents the asynchronous operation. The task's value 43 | /// is a sequence of . 44 | /// 45 | Task> ListAsync(CancellationToken cancel = default(CancellationToken)); 46 | 47 | /// 48 | /// Unpin an object. 49 | /// 50 | /// 51 | /// The CID of the object. 52 | /// 53 | /// 54 | /// true to recursively unpin links of object; otherwise, false to only unpin 55 | /// the specified object. Default is true. 56 | /// 57 | /// 58 | /// Is used to stop the task. When cancelled, the is raised. 59 | /// 60 | /// 61 | /// A task that represents the asynchronous operation. The task's value 62 | /// is a sequence of that were unpinned. 63 | /// 64 | Task> RemoveAsync(Cid id, bool recursive = true, CancellationToken cancel = default(CancellationToken)); 65 | 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /doc/articles/multihash.md: -------------------------------------------------------------------------------- 1 | # MultiHash 2 | 3 | All hashes in IPFS are encoded as a [MultiHash](xref:Ipfs.MultiHash), a self-describing hash format. 4 | The actual [hash function](#algorithms) used depends on security requirements; "sha2-256" is todays default. 5 | 6 | A multihash is used to identify a [peer](xref:Ipfs.Peer), [key](xref:Ipfs.IKey) and [content](cid.md). 7 | For background information, see [hash concept](https://docs.ipfs.io/guides/concepts/hashes/). 8 | 9 | ```csharp 10 | var hello = Encoding.UTF8.GetBytes("Hello world"); 11 | var mh = MultiHash.ComputeHash(hello, "sha2-256"); 12 | ``` 13 | 14 | ## Format 15 | 16 | The binary representation consists of the [hash code](xref:Ipfs.MultiHash.Algorithm), the [digest's](xref:Ipfs.MultiHash.Digest) 17 | length and value. The code and length are encoded as [varints](varint.md). 18 | 19 | The textual representation is usually the [Base58](xref:Ipfs.MultiHash.ToBase58*) encoding of the 20 | binary format. [Base32](xref:Ipfs.MultiHash.ToBase32*) encoding is used when case insensity is required. 21 | 22 | From the above example, the following is produced 23 | 24 | | Name | Value | 25 | | ---- | ----- | 26 | | hash code | 0x12 | 27 | | digest length | 0x20 | 28 | | digest value | 64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c | 29 | | binary | 12 20 64ec88ca00b268e5ba1a35678a1b5316d212f4f366b2477232534a8aeca37f3c | 30 | | base 58 | QmV8cfu6n4NT5xRr2AHdKxFMTZEJrA44qgrBCr739BN9Wb | 31 | | base 32 | ciqgj3eiziale2hfxindkz4kdnjrnuqs6tzwnmshoizfgsuk5srx6pa | 32 | 33 | ## Algorithms 34 | 35 | IPFS assigns a unique [Name](xref:Ipfs.Registry.HashingAlgorithm.Name) and [Code](xref:Ipfs.Registry.HashingAlgorithm.Code) 36 | to a hashing algorithm. See [hashtable.csv](https://github.com/multiformats/multicodec/blob/master/table.csv") 37 | for the currently defined hashing algorithms. 38 | 39 | These algorithms are implemented: 40 | 41 | - blake2b-160, blake2b-256 blake2b-384 and blake2b-512 42 | - blake2s-128, blake2s-160, blake2s-224 a nd blake2s-256 43 | - keccak-224, keccak-256, keccak-384 and keccak-512 44 | - md4 and md5 45 | - sha1 46 | - sha2-256, sha2-512 and dbl-sha2-256 47 | - sha3-224, sha3-256, sha3-384 and sha3-512 48 | - shake-128 and shake-256 49 | 50 | The identity hash is also implemented; which just returns the input bytes. This is used to inline a small amount of 51 | data into a [CID](cid.md). 52 | 53 | ## Registry 54 | 55 | The [hashing registry](xref:Ipfs.Registry.HashingAlgorithm) contains the metadata on hashing algorithms. You can use 56 | [Register](xref:Ipfs.Registry.HashingAlgorithm.Register*) to add a new hashing algorithm. 57 | 58 | ### Example 59 | 60 | Using an hashing algorithm. Note that `ComputeHash` can take a byte array or a `Stream`. 61 | 62 | ```csharp 63 | public void GetHasher() 64 | { 65 | using (var hasher = HashingAlgorithm.GetAlgorithm("sha3-256")) 66 | { 67 | Assert.IsNotNull(hasher); 68 | var input = new byte[] { 0xe9 }; 69 | var expected = "f0d04dd1e6cfc29a4460d521796852f25d9ef8d28b44ee91ff5b759d72c1e6d6".ToHexBuffer(); 70 | 71 | var actual = hasher.ComputeHash(input); 72 | CollectionAssert.AreEqual(expected, actual); 73 | } 74 | } 75 | ``` 76 | -------------------------------------------------------------------------------- /test/DagLinkTest.cs: -------------------------------------------------------------------------------- 1 | using Ipfs; 2 | using Microsoft.VisualStudio.TestTools.UnitTesting; 3 | using System; 4 | using System.Collections.Generic; 5 | using System.Linq; 6 | using System.Text; 7 | using System.IO; 8 | using Google.Protobuf; 9 | 10 | namespace Ipfs 11 | { 12 | [TestClass] 13 | public class DagLinkTest 14 | { 15 | [TestMethod] 16 | public void Creating() 17 | { 18 | var link = new DagLink("abc", "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", 5); 19 | Assert.AreEqual("abc", link.Name); 20 | Assert.AreEqual("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", (string)link.Id); 21 | Assert.AreEqual(5, link.Size); 22 | } 23 | 24 | [TestMethod] 25 | public void Cloning() 26 | { 27 | var link = new DagLink("abc", "QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", 5); 28 | var clone = new DagLink(link); 29 | 30 | Assert.AreEqual("abc", clone.Name); 31 | Assert.AreEqual("QmXg9Pp2ytZ14xgmQjYEiHjVjMFXzCVVEcRTWJBmLgR39V", (string)clone.Id); 32 | Assert.AreEqual(5, clone.Size); 33 | } 34 | 35 | [TestMethod] 36 | public void Encoding() 37 | { 38 | var encoded = "0a22122023dca2a7429612378554b0bb5b85012dec00a17cc2c673f17d2b76a50b839cd51201611803"; 39 | var link = new DagLink("a", "QmQke7LGtfu3GjFP3AnrP8vpEepQ6C5aJSALKAq653bkRi", 3); 40 | var x = link.ToArray(); 41 | Assert.AreEqual(encoded, link.ToArray().ToHexString()); 42 | } 43 | 44 | [TestMethod] 45 | public void Encoding_EmptyName() 46 | { 47 | var encoded = "0a22122023dca2a7429612378554b0bb5b85012dec00a17cc2c673f17d2b76a50b839cd512001803"; 48 | var link = new DagLink("", "QmQke7LGtfu3GjFP3AnrP8vpEepQ6C5aJSALKAq653bkRi", 3); 49 | var x = link.ToArray(); 50 | Assert.AreEqual(encoded, link.ToArray().ToHexString()); 51 | } 52 | 53 | [TestMethod] 54 | public void Encoding_NullName() 55 | { 56 | var encoded = "0a22122023dca2a7429612378554b0bb5b85012dec00a17cc2c673f17d2b76a50b839cd51803"; 57 | var link = new DagLink(null, "QmQke7LGtfu3GjFP3AnrP8vpEepQ6C5aJSALKAq653bkRi", 3); 58 | var x = link.ToArray(); 59 | Assert.AreEqual(encoded, link.ToArray().ToHexString()); 60 | } 61 | 62 | [TestMethod] 63 | public void Null_Stream() 64 | { 65 | ExceptionAssert.Throws(() => new DagLink((CodedInputStream)null)); 66 | ExceptionAssert.Throws(() => new DagLink((Stream)null)); 67 | } 68 | 69 | [TestMethod] 70 | public void Cid_V1() 71 | { 72 | var link = new DagLink("hello", "zB7NCdng5WffuNCgHu4PhDj7nbtuVrhPc2pMhanNxYKRsECdjX9nd44g6CRu2xNrj2bG2NNaTsveL5zDGWhbfiug3VekW", 11); 73 | Assert.AreEqual("hello", link.Name); 74 | Assert.AreEqual(1, link.Id.Version); 75 | Assert.AreEqual("raw", link.Id.ContentType); 76 | Assert.AreEqual("sha2-512", link.Id.Hash.Algorithm.Name); 77 | Assert.AreEqual(11, link.Size); 78 | } 79 | 80 | 81 | } 82 | } 83 | -------------------------------------------------------------------------------- /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 | snk_secret: 12 | secure: 5QzEIgiDqTIrZruPaIQIvTlNMl5BZ7TGEps7ALyBfHE= 13 | git_token: 14 | secure: NeX5NCOUXsCLc1UjTJjqB9F02FZ8Wq0VsxqTXC8kBdyK6zjxjebrf/9Da2sY1Kql 15 | 16 | configuration: Release 17 | os: Visual Studio 2019 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: j4sELCwhVRRjNXFVhjPZjdG4y2itz8jrExhlyDU/lTiLlRQ/P4brB69MGQRFBQae 29 | DOTNET_CLI_TELEMETRY_OPTOUT: 1 30 | 31 | # tools we need for bulding/testing/deploying 32 | install: 33 | # see https://help.appveyor.com/discussions/problems/24758-gitversion-5-not-working-on-visual-studio-2019-build 34 | - choco install gitversion.portable -y --version 4.0.0 35 | - npm install gh-pages@2.0 -g 36 | # No longer signing the assembly 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 | 40 | pull_requests: 41 | do_not_increment_build_number: true 42 | 43 | nuget: 44 | disable_publish_on_pr: true 45 | 46 | before_build: 47 | # - ps: gitversion /output buildserver /updateAssemblyInfo >gitversion.log 48 | - gitversion /output buildserver /updateAssemblyInfo >gitversion 49 | 50 | - echo %GitVersion_MajorMinorPatch% 51 | - echo %GitVersion_NuGetVersion% 52 | 53 | build_script: 54 | - dotnet build -c %CONFIGURATION% -p:Version=%GitVersion_MajorMinorPatch% -p:AssemblyVersion=%GitVersion_MajorMinorPatch% 55 | 56 | after_build: 57 | # Build documentation in doc\_site 58 | - cmd: choco install docfx -y 59 | - cmd: builddocs.cmd 60 | - cmd: 7z a -tzip docs.zip doc\_site 61 | - cmd: appveyor PushArtifact docs.zip 62 | - if defined git_token gh-pages -d doc\_site -m "new docs %GitVersion_FullSemVer%" 63 | 64 | test_script: 65 | - dotnet test -c %CONFIGURATION% --no-build --no-restore test 66 | 67 | after_test: 68 | # Generate coverage report 69 | - dotnet test -c %CONFIGURATION% -f netcoreapp3.0 --no-build --no-restore test /p:CollectCoverage=true /p:Include="[Ipfs.Core]*" 70 | - choco install codecov -y 71 | - codecov -f "test/coverage.opencover.xml" 72 | - dotnet tool install --global coveralls.net --version 1.0.0 73 | - if defined COVERALLS_REPO_TOKEN 74 | csmacnz.coveralls.exe 75 | --opencover -i test/coverage.opencover.xml --useRelativePaths --serviceName appveyor --jobId %APPVEYOR_BUILD_NUMBER% 76 | 77 | artifacts: 78 | - path: 'src/**/*.nupkg' 79 | name: nupkg 80 | - path: 'src/**/*.snupkg' 81 | name: snupkg 82 | 83 | # publish NuGet package on tag build 84 | deploy: 85 | - provider: NuGet 86 | api_key: 87 | secure: OdmGEj/l0K0ZPDmXAYx+fryCzV012eTrM29ALBuL0waxvwLvrufdDXiI+1iNhWEG 88 | on: 89 | appveyor_repo_tag: true 90 | -------------------------------------------------------------------------------- /src/Base58.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ipfs 7 | { 8 | /// 9 | /// A codec for IPFS Base-58. 10 | /// 11 | /// 12 | /// 13 | /// A codec for Base-58, and . Adds the extension method 14 | /// to encode a byte array and to decode a Base-58 string. 15 | /// 16 | /// 17 | /// This is just thin wrapper of . 18 | /// 19 | /// 20 | /// This codec uses the BitCoin alphabet not Flickr's. 21 | /// 22 | /// 23 | public static class Base58 24 | { 25 | /// 26 | /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is 27 | /// encoded with base-58 characters. 28 | /// s 29 | /// 30 | /// An array of 8-bit unsigned integers. 31 | /// 32 | /// 33 | /// The string representation, in base 58, of the contents of . 34 | /// 35 | public static string Encode(byte[] bytes) 36 | { 37 | return SimpleBase.Base58.Bitcoin.Encode(bytes); 38 | } 39 | 40 | /// 41 | /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is 42 | /// encoded with base-58 digits. 43 | /// 44 | /// 45 | /// An array of 8-bit unsigned integers. 46 | /// 47 | /// 48 | /// The string representation, in base 58, of the contents of . 49 | /// 50 | public static string ToBase58(this byte[] bytes) 51 | { 52 | return Encode(bytes); 53 | } 54 | 55 | /// 56 | /// Converts the specified , which encodes binary data as base 58 digits, 57 | /// to an equivalent 8-bit unsigned integer array. 58 | /// 59 | /// 60 | /// The base 58 string to convert. 61 | /// 62 | /// 63 | /// An array of 8-bit unsigned integers that is equivalent to . 64 | /// 65 | public static byte[] Decode(string s) 66 | { 67 | return SimpleBase.Base58.Bitcoin.Decode(s); 68 | } 69 | 70 | /// 71 | /// Converts the specified , which encodes binary data as base 58 digits, 72 | /// to an equivalent 8-bit unsigned integer array. 73 | /// 74 | /// 75 | /// The base 58 string to convert. 76 | /// 77 | /// 78 | /// An array of 8-bit unsigned integers that is equivalent to . 79 | /// 80 | public static byte[] FromBase58(this string s) 81 | { 82 | return Decode(s); 83 | } 84 | } 85 | } 86 | -------------------------------------------------------------------------------- /src/CoreApi/IConfigApi.cs: -------------------------------------------------------------------------------- 1 | using Newtonsoft.Json.Linq; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Ipfs.CoreApi 9 | { 10 | /// 11 | /// Manages the IPFS Configuration. 12 | /// 13 | /// 14 | /// 15 | /// Configuration values are JSON. Json.NET 16 | /// is used to represent JSON. 17 | /// 18 | /// 19 | /// Config API spec 20 | public interface IConfigApi 21 | { 22 | /// 23 | /// Gets the entire configuration. 24 | /// 25 | /// 26 | /// A containing the configuration. 27 | /// 28 | Task GetAsync(CancellationToken cancel = default(CancellationToken)); 29 | 30 | /// 31 | /// Gets the value of a configuration key. 32 | /// 33 | /// 34 | /// The key name, such as "Addresses.API". 35 | /// 36 | /// 37 | /// Is used to stop the task. When cancelled, the is raised. 38 | /// 39 | /// 40 | /// The value of the as . 41 | /// 42 | /// 43 | /// When the does not exist. 44 | /// 45 | /// 46 | /// Keys are case sensistive. 47 | /// 48 | Task GetAsync(string key, CancellationToken cancel = default(CancellationToken)); 49 | 50 | /// 51 | /// Adds or replaces a configuration value. 52 | /// 53 | /// 54 | /// The key name, such as "Addresses.API". 55 | /// 56 | /// 57 | /// The new value of the . 58 | /// 59 | /// 60 | /// Is used to stop the task. When cancelled, the is raised. 61 | /// 62 | Task SetAsync(string key, string value, CancellationToken cancel = default(CancellationToken)); 63 | 64 | /// 65 | /// Adds or replaces a configuration value. 66 | /// 67 | /// 68 | /// The key name, such as "Addresses.API". 69 | /// 70 | /// 71 | /// The new JSON value of the . 72 | /// 73 | /// 74 | /// Is used to stop the task. When cancelled, the is raised. 75 | /// 76 | Task SetAsync(string key, JToken value, CancellationToken cancel = default(CancellationToken)); 77 | 78 | /// 79 | /// Replaces the entire configuration. 80 | /// 81 | /// 82 | Task ReplaceAsync(JObject config); 83 | } 84 | } 85 | -------------------------------------------------------------------------------- /src/Base64NoPad.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ipfs 7 | { 8 | /// 9 | /// A codec for Base-64 (RFC 4648) with no padding. 10 | /// 11 | /// 12 | /// 13 | /// A codec for Base-64, and . Adds the extension method 14 | /// to encode a byte array and to decode a Base-64 string. 15 | /// 16 | /// 17 | public static class Base64NoPad 18 | { 19 | /// 20 | /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is 21 | /// encoded with base-64 characters. 22 | /// s 23 | /// 24 | /// An array of 8-bit unsigned integers. 25 | /// 26 | /// 27 | /// The string representation, in base 64, of the contents of . 28 | /// 29 | public static string Encode(byte[] bytes) 30 | { 31 | return Convert.ToBase64String(bytes) 32 | .TrimEnd('='); 33 | } 34 | 35 | /// 36 | /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is 37 | /// encoded with base-64 digits. 38 | /// 39 | /// 40 | /// An array of 8-bit unsigned integers. 41 | /// s 42 | /// 43 | /// The string representation, in base 64, of the contents of . 44 | /// 45 | public static string ToBase64NoPad(this byte[] bytes) 46 | { 47 | return Encode(bytes); 48 | } 49 | 50 | /// 51 | /// Converts the specified , which encodes binary data as base 64 digits, 52 | /// to an equivalent 8-bit unsigned integer array. 53 | /// 54 | /// 55 | /// The base 64 string to convert. 56 | /// 57 | /// 58 | /// An array of 8-bit unsigned integers that is equivalent to . 59 | /// 60 | public static byte[] Decode(string s) 61 | { 62 | switch (s.Length % 4) // Pad with trailing '='s 63 | { 64 | case 0: break; // No pad chars in this case 65 | case 2: s += "=="; break; // Two pad chars 66 | case 3: s += "="; break; // One pad char 67 | default: throw new Exception("Illegal base64 string!"); 68 | } 69 | 70 | return Convert.FromBase64String(s); // Standard base64 decoder 71 | } 72 | 73 | /// 74 | /// Converts the specified , which encodes binary data as base 64 digits, 75 | /// to an equivalent 8-bit unsigned integer array. 76 | /// 77 | /// 78 | /// The base 64 string to convert. 79 | /// 80 | /// 81 | /// An array of 8-bit unsigned integers that is equivalent to . 82 | /// 83 | public static byte[] FromBase64NoPad(this string s) 84 | { 85 | return Decode(s); 86 | } 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /src/Base32.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ipfs 7 | { 8 | /// 9 | /// A codec for Base-32. 10 | /// 11 | /// 12 | /// 13 | /// A codec for Base-32, and . Adds the extension method 14 | /// to encode a byte array and to decode a Base-32 string. 15 | /// 16 | /// 17 | /// and produce the lower case form of 18 | /// with no padding. 19 | /// and are case-insensitive and 20 | /// allow optional padding. 21 | /// 22 | /// 23 | /// A thin wrapper around . 24 | /// 25 | /// 26 | public static class Base32 27 | { 28 | /// 29 | /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is 30 | /// encoded with base-32 characters. 31 | /// s 32 | /// 33 | /// An array of 8-bit unsigned integers. 34 | /// 35 | /// 36 | /// The string representation, in base 32, of the contents of . 37 | /// 38 | public static string Encode(byte[] input) 39 | { 40 | return SimpleBase.Base32.Rfc4648.Encode(input, false).ToLowerInvariant(); 41 | } 42 | 43 | /// 44 | /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is 45 | /// encoded with base-32 digits. 46 | /// 47 | /// 48 | /// An array of 8-bit unsigned integers. 49 | /// 50 | /// 51 | /// The string representation, in base 32, of the contents of . 52 | /// 53 | public static string ToBase32(this byte[] bytes) 54 | { 55 | return Encode(bytes); 56 | } 57 | 58 | /// 59 | /// Converts the specified , which encodes binary data as base 32 digits, 60 | /// to an equivalent 8-bit unsigned integer array. 61 | /// 62 | /// 63 | /// The base 32 string to convert. 64 | /// 65 | /// 66 | /// An array of 8-bit unsigned integers that is equivalent to . 67 | /// 68 | /// 69 | /// is case-insensitive and allows padding. 70 | /// 71 | public static byte[] Decode(string input) 72 | { 73 | return SimpleBase.Base32.Rfc4648.Decode(input); 74 | } 75 | 76 | /// 77 | /// Converts the specified , which encodes binary data as base 32 digits, 78 | /// to an equivalent 8-bit unsigned integer array. 79 | /// 80 | /// 81 | /// The base 32 string to convert; case-insensitive and allows padding. 82 | /// 83 | /// 84 | /// An array of 8-bit unsigned integers that is equivalent to . 85 | /// 86 | public static byte[] FromBase32(this string s) 87 | { 88 | return Decode(s); 89 | } 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /src/IpfsCore.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | netstandard14;netstandard2;net45;netcoreapp3.0 5 | Ipfs.Core 6 | Ipfs 7 | portable 8 | true 9 | 10 | 11 | 0.42 12 | 0.42 13 | 14 | 15 | Ipfs.Core 16 | Richard Schneider 17 | IPFS Core Objects 18 | 19 | Core objects and interfaces for IPFS. 20 | 21 | 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. 22 | 23 | false 24 | https://github.com/richardschneider/net-ipfs-core/releases 25 | © 2015-2019 Richard Schneider 26 | ipfs peer-to-peer distributed file-system 27 | True 28 | https://github.com/richardschneider/net-ipfs-core 29 | https://raw.githubusercontent.com/richardschneider/net-ipfs-core/master/doc/images/ipfs-cs-logo-64x64.png 30 | 31 | true 32 | true 33 | 34 | 35 | 36 | 37 | true 38 | 39 | false 40 | 41 | true 42 | snupkg 43 | .pdb;$(AllowedOutputExtensionsInPackageBuildOutputFolder) 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | NETSTANDARD14 65 | 66 | 67 | 68 | ASYNCSTREAM 69 | 70 | 71 | 72 | -------------------------------------------------------------------------------- /.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 | # Visual Studo 2015 cache/options directory 26 | .vs/ 27 | 28 | # MSTest test Results 29 | [Tt]est[Rr]esult*/ 30 | [Bb]uild[Ll]og.* 31 | 32 | # NUNIT 33 | *.VisualState.xml 34 | TestResult.xml 35 | 36 | # Build Results of an ATL Project 37 | [Dd]ebugPS/ 38 | [Rr]eleasePS/ 39 | dlldata.c 40 | 41 | *_i.c 42 | *_p.c 43 | *_i.h 44 | *.ilk 45 | *.meta 46 | *.obj 47 | *.pch 48 | *.pdb 49 | *.pgc 50 | *.pgd 51 | *.rsp 52 | *.sbr 53 | *.tlb 54 | *.tli 55 | *.tlh 56 | *.tmp 57 | *.tmp_proj 58 | *.log 59 | *.vspscc 60 | *.vssscc 61 | .builds 62 | *.pidb 63 | *.svclog 64 | *.scc 65 | 66 | # Chutzpah Test files 67 | _Chutzpah* 68 | 69 | # Visual C++ cache files 70 | ipch/ 71 | *.aps 72 | *.ncb 73 | *.opensdf 74 | *.sdf 75 | *.cachefile 76 | 77 | # Visual Studio profiler 78 | *.psess 79 | *.vsp 80 | *.vspx 81 | 82 | # TFS 2012 Local Workspace 83 | $tf/ 84 | 85 | # Guidance Automation Toolkit 86 | *.gpState 87 | 88 | # ReSharper is a .NET coding add-in 89 | _ReSharper*/ 90 | *.[Rr]e[Ss]harper 91 | *.DotSettings.user 92 | 93 | # JustCode is a .NET coding addin-in 94 | .JustCode 95 | 96 | # TeamCity is a build add-in 97 | _TeamCity* 98 | 99 | # DotCover is a Code Coverage Tool 100 | *.dotCover 101 | 102 | # NCrunch 103 | _NCrunch_* 104 | .*crunch*.local.xml 105 | 106 | # MightyMoose 107 | *.mm.* 108 | AutoTest.Net/ 109 | 110 | # Web workbench (sass) 111 | .sass-cache/ 112 | 113 | # Installshield output folder 114 | [Ee]xpress/ 115 | 116 | # DocProject is a documentation generator add-in 117 | DocProject/buildhelp/ 118 | DocProject/Help/*.HxT 119 | DocProject/Help/*.HxC 120 | DocProject/Help/*.hhc 121 | DocProject/Help/*.hhk 122 | DocProject/Help/*.hhp 123 | DocProject/Help/Html2 124 | DocProject/Help/html 125 | 126 | # Click-Once directory 127 | publish/ 128 | 129 | # Publish Web Output 130 | *.[Pp]ublish.xml 131 | *.azurePubxml 132 | # TODO: Comment the next line if you want to checkin your web deploy settings 133 | # but database connection strings (with potential passwords) will be unencrypted 134 | *.pubxml 135 | *.publishproj 136 | 137 | # NuGet Packages 138 | *.nupkg 139 | # The packages folder can be ignored because of Package Restore 140 | **/packages/* 141 | # except build/, which is used as an MSBuild target. 142 | !**/packages/build/ 143 | # Uncomment if necessary however generally it will be regenerated when needed 144 | #!**/packages/repositories.config 145 | 146 | # Windows Azure Build Output 147 | csx/ 148 | *.build.csdef 149 | 150 | # Windows Store app package directory 151 | AppPackages/ 152 | 153 | # Others 154 | *.[Cc]ache 155 | ClientBin/ 156 | [Ss]tyle[Cc]op.* 157 | ~$* 158 | *~ 159 | *.dbmdl 160 | *.dbproj.schemaview 161 | *.pfx 162 | *.publishsettings 163 | node_modules/ 164 | bower_components/ 165 | 166 | # RIA/Silverlight projects 167 | Generated_Code/ 168 | 169 | # Backup & report files from converting an old project file 170 | # to a newer Visual Studio version. Backup files are not needed, 171 | # because we have git ;-) 172 | _UpgradeReport_Files/ 173 | Backup*/ 174 | UpgradeLog*.XML 175 | UpgradeLog*.htm 176 | 177 | # SQL Server files 178 | *.mdf 179 | *.ldf 180 | 181 | # Business Intelligence projects 182 | *.rdl.data 183 | *.bim.layout 184 | *.bim_*.settings 185 | 186 | # Microsoft Fakes 187 | FakesAssemblies/ 188 | 189 | # Node.js Tools for Visual Studio 190 | .ntvs_analysis.dat 191 | 192 | # Visual Studio 6 build log 193 | *.plg 194 | 195 | # Visual Studio 6 workspace options file 196 | *.opt 197 | 198 | # VS 2017 creates this 199 | src/Ipfs.Core.xml 200 | -------------------------------------------------------------------------------- /src/CoreApi/AddFileOptions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs.CoreApi 6 | { 7 | /// 8 | /// The options when adding data to the IPFS file system. 9 | /// 10 | /// 11 | public class AddFileOptions 12 | { 13 | /// 14 | /// Determines if the data is pinned to local storage. 15 | /// 16 | /// 17 | /// If true the data is pinned to local storage and will not be 18 | /// garbage collected. The default is true. 19 | /// 20 | public bool Pin { get; set; } = true; 21 | 22 | /// 23 | /// The maximum number of data bytes in a block. 24 | /// 25 | /// 26 | /// The default is 256 * 1024 (‭262,144) bytes.‬ 27 | /// 28 | public int ChunkSize { get; set; } = 256 * 1024; 29 | 30 | /// 31 | /// Determines if the trickle-dag format is used for dag generation. 32 | /// 33 | /// 34 | /// The default is false. 35 | /// 36 | public bool Trickle { get; set; } = false; 37 | 38 | /// 39 | /// Determines if added file(s) are wrapped in a directory object. 40 | /// 41 | /// 42 | /// The default is false. 43 | /// 44 | public bool Wrap { get; set; } = false; 45 | 46 | /// 47 | /// Determines if raw blocks are used for leaf data blocks. 48 | /// 49 | /// 50 | /// The default is false. 51 | /// 52 | /// 53 | /// RawLeaves and are mutually exclusive. 54 | /// 55 | public bool RawLeaves { get; set; } = false; 56 | 57 | /// 58 | /// The hashing algorithm name to use. 59 | /// 60 | /// 61 | /// The algorithm name used to produce the . 62 | /// Defaults to . 63 | /// 64 | /// 65 | public string Hash { get; set; } = MultiHash.DefaultAlgorithmName; 66 | 67 | /// 68 | /// The encoding algorithm name to use. 69 | /// 70 | /// 71 | /// The algorithm name used to produce the . 72 | /// Defaults to . 73 | /// 74 | /// 75 | public string Encoding { get; set; } = MultiBase.DefaultAlgorithmName; 76 | 77 | /// 78 | /// Determines if only file information is produced. 79 | /// 80 | /// 81 | /// If true no data is added to IPFS. The default is false. 82 | /// 83 | public bool OnlyHash { get; set; } = false; 84 | 85 | /// 86 | /// The key name used to protect (encrypt) the file contents. 87 | /// 88 | /// 89 | /// The name of an existing key. 90 | /// 91 | /// 92 | /// ProtectionKey and are mutually exclusive. 93 | /// 94 | /// 95 | public string ProtectionKey { get; set; } 96 | 97 | /// 98 | /// Used to report the progress of a file transfer. 99 | /// 100 | public IProgress Progress { get; set; } 101 | } 102 | } 103 | -------------------------------------------------------------------------------- /test/DurationTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using System.IO; 7 | 8 | namespace Ipfs 9 | { 10 | 11 | [TestClass] 12 | public class DurationTest 13 | { 14 | [TestMethod] 15 | public void Parsing_Examples() 16 | { 17 | Assert.AreEqual(TimeSpan.FromMilliseconds(300), Duration.Parse("300ms")); 18 | Assert.AreEqual(TimeSpan.FromHours(-1.5), Duration.Parse("-1.5h")); 19 | Assert.AreEqual(new TimeSpan(2, 45, 0), Duration.Parse("2h45m")); 20 | Assert.AreEqual(new TimeSpan(0, 1, 0) + TimeSpan.FromSeconds(4.483878032), Duration.Parse("1m4.483878032s")); 21 | } 22 | 23 | [TestMethod] 24 | public void Parsing_Zero() 25 | { 26 | Assert.AreEqual(TimeSpan.Zero, Duration.Parse("0s")); 27 | Assert.AreEqual(TimeSpan.Zero, Duration.Parse("0µs")); 28 | Assert.AreEqual(TimeSpan.Zero, Duration.Parse("0ns")); 29 | Assert.AreEqual(TimeSpan.Zero, Duration.Parse("n/a")); 30 | Assert.AreEqual(TimeSpan.Zero, Duration.Parse("unknown")); 31 | Assert.AreEqual(TimeSpan.Zero, Duration.Parse("")); 32 | } 33 | 34 | [TestMethod] 35 | public void Parsing_Negative() 36 | { 37 | Assert.AreEqual(TimeSpan.FromHours(-2), Duration.Parse("-1.5h30m")); 38 | } 39 | 40 | [TestMethod] 41 | public void Parsing_Submilliseconds() 42 | { 43 | // Note: resolution of TimeSpan is 100ns, e.g. 1 tick. 44 | Assert.AreEqual(TimeSpan.FromTicks(2), Duration.Parse("200ns")); 45 | Assert.AreEqual(TimeSpan.FromTicks(2000), Duration.Parse("200us")); 46 | Assert.AreEqual(TimeSpan.FromTicks(2000), Duration.Parse("200µs")); 47 | } 48 | 49 | [TestMethod] 50 | public void Parsing_MissingNumber() 51 | { 52 | ExceptionAssert.Throws(() => 53 | { 54 | var _ = Duration.Parse("s"); 55 | }); 56 | } 57 | 58 | [TestMethod] 59 | public void Parsing_MissingUnit() 60 | { 61 | ExceptionAssert.Throws(() => 62 | { 63 | var _ = Duration.Parse("1"); 64 | }, "Missing IPFS duration unit."); 65 | } 66 | 67 | [TestMethod] 68 | public void Parsing_InvalidUnit() 69 | { 70 | ExceptionAssert.Throws(() => 71 | { 72 | var _ = Duration.Parse("1jiffy"); 73 | }, "Unknown IPFS duration unit 'jiffy'."); 74 | } 75 | 76 | [TestMethod] 77 | public void Stringify() 78 | { 79 | Assert.AreEqual("0s", Duration.Stringify(TimeSpan.Zero)); 80 | Assert.AreEqual("n/a", Duration.Stringify(TimeSpan.Zero, "n/a")); 81 | 82 | Assert.AreEqual("2h", Duration.Stringify(new TimeSpan(2, 0, 0))); 83 | Assert.AreEqual("3m", Duration.Stringify(new TimeSpan(0, 3, 0))); 84 | Assert.AreEqual("4s", Duration.Stringify(new TimeSpan(0, 0, 4))); 85 | Assert.AreEqual("5ms", Duration.Stringify(new TimeSpan(0, 0, 0, 0, 5))); 86 | Assert.AreEqual("2h4s", Duration.Stringify(new TimeSpan(2, 0, 4))); 87 | Assert.AreEqual("26h3m4s5ms", Duration.Stringify(new TimeSpan(1, 2, 3, 4, 5))); 88 | 89 | Assert.AreEqual("-48h", Duration.Stringify(TimeSpan.FromDays(-2))); 90 | Assert.AreEqual("-2h", Duration.Stringify(TimeSpan.FromHours(-2))); 91 | Assert.AreEqual("-1h30m", Duration.Stringify(TimeSpan.FromHours(-1.5))); 92 | 93 | Assert.AreEqual("200ns", Duration.Stringify(TimeSpan.FromTicks(2))); 94 | Assert.AreEqual("200us", Duration.Stringify(TimeSpan.FromTicks(2000))); 95 | Assert.AreEqual("200us300ns", Duration.Stringify(TimeSpan.FromTicks(2003))); 96 | } 97 | } 98 | } 99 | 100 | -------------------------------------------------------------------------------- /src/Base64Url.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | 6 | namespace Ipfs 7 | { 8 | /// 9 | /// A codec for Base-64 URL (RFC 4648). 10 | /// 11 | /// 12 | /// 13 | /// A codec for Base-64 URL, and . Adds the extension method 14 | /// to encode a byte array and to decode a Base-64 URL string. 15 | /// 16 | /// 17 | /// The original code was found at . 18 | /// 19 | /// 20 | public static class Base64Url 21 | { 22 | /// 23 | /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is 24 | /// encoded with base-64 URL characters. 25 | /// s 26 | /// 27 | /// An array of 8-bit unsigned integers. 28 | /// 29 | /// 30 | /// The string representation, in base 64, of the contents of . 31 | /// 32 | public static string Encode(byte[] bytes) 33 | { 34 | string s = Convert.ToBase64String(bytes); // Standard base64 encoder 35 | 36 | s = s.TrimEnd('='); // Remove any trailing '='s 37 | s = s.Replace('+', '-'); // 62nd char of encoding 38 | s = s.Replace('/', '_'); // 63rd char of encoding 39 | 40 | return s; 41 | } 42 | 43 | /// 44 | /// Converts an array of 8-bit unsigned integers to its equivalent string representation that is 45 | /// encoded with base-64 URL digits. 46 | /// 47 | /// 48 | /// An array of 8-bit unsigned integers. 49 | /// s 50 | /// 51 | /// The string representation, in base 64, of the contents of . 52 | /// 53 | public static string ToBase64Url(this byte[] bytes) 54 | { 55 | return Encode(bytes); 56 | } 57 | 58 | /// 59 | /// Converts the specified , which encodes binary data as base 64 URL digits, 60 | /// to an equivalent 8-bit unsigned integer array. 61 | /// 62 | /// 63 | /// The base 64 string to convert. 64 | /// 65 | /// 66 | /// An array of 8-bit unsigned integers that is equivalent to . 67 | /// 68 | public static byte[] Decode(string s) 69 | { 70 | s = s.Replace('-', '+'); // 62nd char of encoding 71 | s = s.Replace('_', '/'); // 63rd char of encoding 72 | 73 | switch (s.Length % 4) // Pad with trailing '='s 74 | { 75 | case 0: break; // No pad chars in this case 76 | case 2: s += "=="; break; // Two pad chars 77 | case 3: s += "="; break; // One pad char 78 | default: throw new Exception("Illegal base64url string!"); 79 | } 80 | 81 | return Convert.FromBase64String(s); // Standard base64 decoder 82 | } 83 | 84 | /// 85 | /// Converts the specified , which encodes binary data as base 64 url digits, 86 | /// to an equivalent 8-bit unsigned integer array. 87 | /// 88 | /// 89 | /// The base 64 string to convert. 90 | /// 91 | /// 92 | /// An array of 8-bit unsigned integers that is equivalent to . 93 | /// 94 | public static byte[] FromBase64Url(this string s) 95 | { 96 | return Decode(s); 97 | } 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /src/MultiCodec.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | using System.Text; 6 | using System.Threading.Tasks; 7 | using Ipfs.Registry; 8 | using Google.Protobuf; 9 | 10 | namespace Ipfs 11 | { 12 | /// 13 | /// Wraps other formats with a tiny bit of self-description. 14 | /// 15 | /// 16 | /// MultiCodec is a self-describing multiformat, it wraps other formats with a 17 | /// tiny bit of self-description. A multicodec identifier is both a varint and the code 18 | /// identifying the following data. 19 | /// 20 | /// Adds the following extension methods to 21 | /// 22 | /// ReadMultiCodec 23 | /// 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// 29 | public static class MultiCodec 30 | { 31 | 32 | /// 33 | /// Reads a from the . 34 | /// 35 | /// 36 | /// A multicodec encoded . 37 | /// 38 | /// The codec. 39 | /// 40 | /// If the code does not exist, a new is 41 | /// registered with the "codec-x"; where 42 | /// 'x' is the code's decimal represention. 43 | /// 44 | public static Codec ReadMultiCodec(this Stream stream) 45 | { 46 | var code = stream.ReadVarint32(); 47 | Codec.Codes.TryGetValue(code, out Codec codec); 48 | if (codec == null) 49 | { 50 | codec = Codec.Register($"codec-{code}", code); 51 | } 52 | return codec; 53 | } 54 | 55 | /// 56 | /// Reads a from the . 57 | /// 58 | /// 59 | /// A multicodec encoded . 60 | /// 61 | /// The codec. 62 | /// 63 | /// If the code does not exist, a new is 64 | /// registered with the "codec-x"; where 65 | /// 'x' is the code's decimal represention. 66 | /// 67 | public static Codec ReadMultiCodec(this CodedInputStream stream) 68 | { 69 | var code = stream.ReadInt32(); 70 | Codec.Codes.TryGetValue(code, out Codec codec); 71 | if (codec == null) 72 | { 73 | codec = Codec.Register($"codec-{code}", code); 74 | } 75 | return codec; 76 | } 77 | 78 | /// 79 | /// Writes a to the . 80 | /// 81 | /// 82 | /// A multicodec encoded . 83 | /// 84 | /// 85 | /// The . 86 | /// 87 | /// 88 | /// Writes the of the to 89 | /// the . 90 | /// 91 | /// 92 | /// When is not registered. 93 | /// 94 | public static void WriteMultiCodec(this Stream stream, string name) 95 | { 96 | Codec.Names.TryGetValue(name, out Codec codec); 97 | if (codec == null) 98 | { 99 | throw new KeyNotFoundException($"Codec '{name}' is not registered."); 100 | } 101 | stream.WriteVarint(codec.Code); 102 | } 103 | 104 | 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /src/CoreApi/IBootstrapApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.CoreApi 8 | { 9 | /// 10 | /// Manages the list of initial peers. 11 | /// 12 | /// 13 | /// The API manipulates the "bootstrap list", which contains 14 | /// the addresses of the bootstrap nodes. These are the trusted peers from 15 | /// which to learn about other peers in the network. 16 | /// 17 | /// 18 | /// Bootstrap API spec 19 | public interface IBootstrapApi 20 | { 21 | /// 22 | /// Adds a new peer. 23 | /// 24 | /// 25 | /// The address must end with the ipfs protocol and the public ID 26 | /// of the peer. For example "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" 27 | /// 28 | /// 29 | /// Is used to stop the task. When cancelled, the is raised. 30 | /// 31 | /// 32 | /// A task that represents the asynchronous operation. The task's result is 33 | /// the address that was added or null if the address is already 34 | /// in the bootstrap list. 35 | /// 36 | Task AddAsync(MultiAddress address, CancellationToken cancel = default(CancellationToken)); 37 | 38 | /// 39 | /// Adds the default peers to the list. 40 | /// 41 | /// 42 | /// Is used to stop the task. When cancelled, the is raised. 43 | /// 44 | /// 45 | /// A task that represents the asynchronous operation. The task's result is 46 | /// the sequence of addresses that were added. 47 | /// 48 | Task> AddDefaultsAsync(CancellationToken cancel = default(CancellationToken)); 49 | 50 | /// 51 | /// List all the peers. 52 | /// 53 | /// 54 | /// Is used to stop the task. When cancelled, the is raised. 55 | /// 56 | /// 57 | /// A task that represents the asynchronous operation. The task's result is 58 | /// a sequence of addresses. 59 | /// 60 | Task> ListAsync(CancellationToken cancel = default(CancellationToken)); 61 | 62 | /// 63 | /// Delete the specified peer. 64 | /// 65 | /// 66 | /// The address must end with the ipfs protocol and the public ID 67 | /// of the peer. For example "/ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ" 68 | /// 69 | /// 70 | /// Is used to stop the task. When cancelled, the is raised. 71 | /// 72 | /// 73 | /// A task that represents the asynchronous operation. The task's result is 74 | /// the address that was removed or null if the 75 | /// is not in the bootstrap list. 76 | /// 77 | Task RemoveAsync(MultiAddress address, CancellationToken cancel = default(CancellationToken)); 78 | 79 | /// 80 | /// Remove all the peers. 81 | /// 82 | /// 83 | /// Is used to stop the task. When cancelled, the is raised. 84 | /// 85 | /// 86 | /// A task that represents the asynchronous operation. 87 | /// 88 | Task RemoveAllAsync(CancellationToken cancel = default(CancellationToken)); 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /src/CoreApi/IBitswapApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.CoreApi 8 | { 9 | /// 10 | /// Data trading module for IPFS. Its purpose is to request blocks from and 11 | /// send blocks to other peers in the network. 12 | /// 13 | /// 14 | /// Bitswap has two primary jobs 15 | /// 16 | /// 17 | /// 18 | /// Attempt to acquire blocks from the network that have been requested by the client 19 | /// 20 | /// 21 | /// 22 | /// 23 | /// Judiciously (though strategically) send blocks in its possession to other peers who want them 24 | /// 25 | /// 26 | /// 27 | /// 28 | /// Bitswap spec 29 | public interface IBitswapApi 30 | { 31 | /// 32 | /// Gets a block from the IPFS network. 33 | /// 34 | /// 35 | /// The of the block. 36 | /// 37 | /// 38 | /// Is used to stop the task. When cancelled, the is raised. 39 | /// 40 | /// 41 | /// A task that represents the asynchronous get operation. The task's value 42 | /// contains the block's id and data. 43 | /// 44 | /// 45 | /// Waits for another peer to supply the block with the . 46 | /// 47 | Task GetAsync(Cid id, CancellationToken cancel = default(CancellationToken)); 48 | 49 | /// 50 | /// The blocks that are needed by a peer. 51 | /// 52 | /// 53 | /// The id of an IPFS peer. If not specified (e.g. null), then the local 54 | /// peer is used. 55 | /// 56 | /// 57 | /// Is used to stop the task. When cancelled, the is raised. 58 | /// 59 | /// 60 | /// A task that represents the asynchronous operation. The task's value 61 | /// contains the sequence of blocks needed by the . 62 | /// 63 | Task> WantsAsync(MultiHash peer = null, CancellationToken cancel = default(CancellationToken)); 64 | 65 | /// 66 | /// Remove the CID from the want list. 67 | /// 68 | /// 69 | /// The content that is no longer needed. 70 | /// 71 | /// 72 | /// Is used to stop the task. When cancelled, the is raised. 73 | /// 74 | /// 75 | /// A task that represents the asynchronous operation. 76 | /// 77 | /// 78 | /// Any outstanding for the 79 | /// are cancelled. 80 | /// 81 | Task UnwantAsync(Cid id, CancellationToken cancel = default(CancellationToken)); 82 | 83 | /// 84 | /// Gets information on the blocks exchanged with a specific . 85 | /// 86 | /// 87 | /// The peer to get information on. If the peer is unknown, then a ledger 88 | /// with zeros is returned. 89 | /// 90 | /// 91 | /// Is used to stop the task. When cancelled, the is raised. 92 | /// 93 | /// 94 | /// A task that represents the asynchronous operation. The task's value 95 | /// contains the for the . 96 | /// 97 | Task LedgerAsync(Peer peer, CancellationToken cancel = default(CancellationToken)); 98 | } 99 | } 100 | -------------------------------------------------------------------------------- /test/Registry/HashingAlgorithmTest.cs: -------------------------------------------------------------------------------- 1 | using Microsoft.VisualStudio.TestTools.UnitTesting; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using System.Text; 6 | using System.IO; 7 | using Google.Protobuf; 8 | 9 | namespace Ipfs.Registry 10 | { 11 | [TestClass] 12 | public class HashingAlgorithmTest 13 | { 14 | [TestMethod] 15 | public void GetHasher() 16 | { 17 | using (var hasher = HashingAlgorithm.GetAlgorithm("sha3-256")) 18 | { 19 | Assert.IsNotNull(hasher); 20 | var input = new byte[] { 0xe9 }; 21 | var expected = "f0d04dd1e6cfc29a4460d521796852f25d9ef8d28b44ee91ff5b759d72c1e6d6".ToHexBuffer(); 22 | 23 | var actual = hasher.ComputeHash(input); 24 | CollectionAssert.AreEqual(expected, actual); 25 | } 26 | } 27 | 28 | [TestMethod] 29 | public void GetHasher_Unknown() 30 | { 31 | ExceptionAssert.Throws(() => HashingAlgorithm.GetAlgorithm("unknown")); 32 | } 33 | 34 | [TestMethod] 35 | public void GetMetadata() 36 | { 37 | var info = HashingAlgorithm.GetAlgorithmMetadata("sha3-256"); 38 | Assert.IsNotNull(info); 39 | Assert.AreEqual("sha3-256", info.Name); 40 | Assert.AreEqual(0x16, info.Code); 41 | Assert.AreEqual(256 /8, info.DigestSize); 42 | Assert.IsNotNull(info.Hasher); 43 | } 44 | 45 | [TestMethod] 46 | public void GetMetadata_Unknown() 47 | { 48 | ExceptionAssert.Throws(() => HashingAlgorithm.GetAlgorithmMetadata("unknown")); 49 | } 50 | 51 | [TestMethod] 52 | public void GetMetadata_Alias() 53 | { 54 | var info = HashingAlgorithm.GetAlgorithmMetadata("id"); 55 | Assert.IsNotNull(info); 56 | Assert.AreEqual("identity", info.Name); 57 | Assert.AreEqual(0, info.Code); 58 | Assert.AreEqual(0, info.DigestSize); 59 | Assert.IsNotNull(info.Hasher); 60 | } 61 | 62 | [TestMethod] 63 | public void HashingAlgorithm_Bad_Name() 64 | { 65 | ExceptionAssert.Throws(() => HashingAlgorithm.Register(null, 1, 1)); 66 | ExceptionAssert.Throws(() => HashingAlgorithm.Register("", 1, 1)); 67 | ExceptionAssert.Throws(() => HashingAlgorithm.Register(" ", 1, 1)); 68 | } 69 | 70 | [TestMethod] 71 | public void HashingAlgorithm_Name_Already_Exists() 72 | { 73 | ExceptionAssert.Throws(() => HashingAlgorithm.Register("sha1", 0x11, 1)); 74 | } 75 | 76 | [TestMethod] 77 | public void HashingAlgorithm_Number_Already_Exists() 78 | { 79 | ExceptionAssert.Throws(() => HashingAlgorithm.Register("sha1-x", 0x11, 1)); 80 | } 81 | 82 | [TestMethod] 83 | public void HashingAlgorithms_Are_Enumerable() 84 | { 85 | Assert.IsTrue(5 <= HashingAlgorithm.All.Count()); 86 | } 87 | 88 | [TestMethod] 89 | public void HashingAlgorithm_Bad_Alias() 90 | { 91 | ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias(null, "sha1")); 92 | ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias("", "sha1")); 93 | ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias(" ", "sha1")); 94 | } 95 | 96 | [TestMethod] 97 | public void HashingAlgorithm_Alias_Already_Exists() 98 | { 99 | ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias("id", "identity")); 100 | } 101 | 102 | [TestMethod] 103 | public void HashingAlgorithm_Alias_Target_Does_Not_Exist() 104 | { 105 | ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias("foo", "sha1-x")); 106 | } 107 | 108 | [TestMethod] 109 | public void HashingAlgorithm_Alias_Target_Is_Bad() 110 | { 111 | ExceptionAssert.Throws(() => HashingAlgorithm.RegisterAlias("foo", " ")); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /src/CoreApi/INameApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.CoreApi 8 | { 9 | /// 10 | /// Manages the IPNS (Interplanetary Name Space). 11 | /// 12 | /// 13 | /// IPNS is a PKI namespace, where names are the hashes of public keys, and 14 | /// the private key enables publishing new(signed) values. The default name 15 | /// is the node's own , 16 | /// which is the hash of its public key. 17 | /// 18 | /// Name API spec 19 | public interface INameApi 20 | { 21 | /// 22 | /// Publish an IPFS name. 23 | /// 24 | /// 25 | /// The CID or path to the content to publish. 26 | /// 27 | /// 28 | /// Resolve before publishing. Defaults to true. 29 | /// 30 | /// 31 | /// The local key name used to sign the content. Defaults to "self". 32 | /// 33 | /// 34 | /// Duration that the record will be valid for. Defaults to 24 hours. 35 | /// 36 | /// 37 | /// Is used to stop the task. When cancelled, the is raised. 38 | /// 39 | /// 40 | /// A task that represents the asynchronous operation. The task's value is 41 | /// the of the published content. 42 | /// 43 | Task PublishAsync( 44 | string path, 45 | bool resolve = true, 46 | string key = "self", 47 | TimeSpan? lifetime = null, 48 | CancellationToken cancel = default(CancellationToken) 49 | ); 50 | 51 | /// 52 | /// Publish an IPFS name. 53 | /// 54 | /// 55 | /// The of the content to publish. 56 | /// 57 | /// 58 | /// The local key name used to sign the content. Defaults to "self". 59 | /// 60 | /// 61 | /// Duration that the record will be valid for. Defaults to 24 hours. 62 | /// 63 | /// 64 | /// Is used to stop the task. When cancelled, the is raised. 65 | /// 66 | /// 67 | /// A task that represents the asynchronous operation. The task's value is 68 | /// the of the published content. 69 | /// 70 | Task PublishAsync( 71 | Cid id, 72 | string key = "self", 73 | TimeSpan? lifetime = null, 74 | CancellationToken cancel = default(CancellationToken) 75 | ); 76 | 77 | /// 78 | /// Resolve an IPNS name. 79 | /// 80 | /// 81 | /// An IPNS address, such as: /ipns/ipfs.io or a CID. 82 | /// 83 | /// 84 | /// Resolve until the result is not an IPNS name. Defaults to false. 85 | /// 86 | /// 87 | /// Do not use cached entries. Defaults to false. 88 | /// 89 | /// 90 | /// Is used to stop the task. When cancelled, the is raised. 91 | /// 92 | /// 93 | /// A task that represents the asynchronous operation. The task's value is 94 | /// the resolved path as a , such as 95 | /// /ipfs/QmYNQJoKGNHTpPxCBPh9KkDpaExgd2duMa3aF6ytMpHdao. 96 | /// 97 | Task ResolveAsync( 98 | string name, 99 | bool recursive = false, 100 | bool nocache = false, 101 | CancellationToken cancel = default(CancellationToken) 102 | ); 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /src/MultiBase.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.Threading.Tasks; 6 | using Ipfs.Registry; 7 | 8 | namespace Ipfs 9 | { 10 | /// 11 | /// Self identifying base encodings. 12 | /// 13 | /// 14 | /// MultiBase is a protocol for distinguishing base encodings 15 | /// and other simple string encodings. 16 | /// See the registry for supported algorithms. 17 | /// 18 | /// 19 | public static class MultiBase 20 | { 21 | /// 22 | /// The default multi-base algorithm is "base58btc". 23 | /// 24 | public const string DefaultAlgorithmName = "base58btc"; 25 | 26 | /// 27 | /// Gets the with the specified IPFS multi-hash name. 28 | /// 29 | /// 30 | /// The name of an algorithm, see 31 | /// for 32 | /// for IPFS defined names. 33 | /// 34 | /// 35 | /// When is not registered. 36 | /// 37 | static MultiBaseAlgorithm GetAlgorithm(string name) 38 | { 39 | try 40 | { 41 | return MultiBaseAlgorithm.Names[name]; 42 | } 43 | catch (KeyNotFoundException) 44 | { 45 | throw new KeyNotFoundException($"MutiBase algorithm '{name}' is not registered."); 46 | } 47 | } 48 | 49 | /// 50 | /// Converts an array of 8-bit unsigned integers to its equivalent string representation. 51 | /// 52 | /// 53 | /// An array of 8-bit unsigned integers. 54 | /// 55 | /// 56 | /// The name of the multi-base algorithm to use. See . 57 | /// 58 | /// 59 | /// A starting with the algorithm's and 60 | /// followed by the encoded string representation of the . 61 | /// 62 | /// 63 | /// When is not registered. 64 | /// 65 | public static string Encode(byte[] bytes, string algorithmName = DefaultAlgorithmName) 66 | { 67 | if (bytes == null) 68 | { 69 | throw new ArgumentNullException("bytes"); 70 | } 71 | 72 | var alg = GetAlgorithm(algorithmName); 73 | return alg.Code + alg.Encode(bytes); 74 | } 75 | 76 | /// 77 | /// Converts the specified , which encodes binary data, 78 | /// to an equivalent 8-bit unsigned integer array. 79 | /// 80 | /// 81 | /// The multi-base string to convert. 82 | /// 83 | /// 84 | /// An array of 8-bit unsigned integers that is equivalent to . 85 | /// 86 | /// 87 | /// When the can not be decoded. 88 | /// 89 | public static byte[] Decode(string s) 90 | { 91 | if (string.IsNullOrWhiteSpace(s)) 92 | { 93 | throw new ArgumentNullException("s"); 94 | } 95 | 96 | MultiBaseAlgorithm.Codes.TryGetValue(s[0], out MultiBaseAlgorithm alg); 97 | if (alg == null) 98 | { 99 | throw new FormatException($"MultiBase '{s}' is invalid. The code is not registered."); 100 | } 101 | 102 | try 103 | { 104 | return alg.Decode(s.Substring(1)); 105 | } 106 | catch (Exception e) 107 | { 108 | throw new FormatException($"MultiBase '{s}' is invalid; decode failed.", e); 109 | } 110 | } 111 | 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /test/VarintTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using System.IO; 7 | using System.Threading; 8 | using System.Threading.Tasks; 9 | 10 | namespace Ipfs 11 | { 12 | [TestClass] 13 | public class VarintTest 14 | { 15 | [TestMethod] 16 | public void Zero() 17 | { 18 | var x = new byte[] { 0 }; 19 | Assert.AreEqual(1, Varint.RequiredBytes(0)); 20 | CollectionAssert.AreEqual(x, Varint.Encode(0)); 21 | Assert.AreEqual(0, Varint.DecodeInt32(x)); 22 | } 23 | 24 | [TestMethod] 25 | public void ThreeHundred() 26 | { 27 | var x = new byte[] { 0xAC, 0x02 }; 28 | Assert.AreEqual(2, Varint.RequiredBytes(300)); 29 | CollectionAssert.AreEqual(x, Varint.Encode(300)); 30 | Assert.AreEqual(300, Varint.DecodeInt32(x)); 31 | } 32 | 33 | [TestMethod] 34 | public void Decode_From_Offset() 35 | { 36 | var x = new byte[] { 0x00, 0xAC, 0x02 }; 37 | Assert.AreEqual(300, Varint.DecodeInt32(x, 1)); 38 | } 39 | 40 | [TestMethod] 41 | public void MaxLong() 42 | { 43 | var x = "ffffffffffffffff7f".ToHexBuffer(); 44 | Assert.AreEqual(9, Varint.RequiredBytes(long.MaxValue)); 45 | CollectionAssert.AreEqual(x, Varint.Encode(long.MaxValue)); 46 | Assert.AreEqual(long.MaxValue, Varint.DecodeInt64(x)); 47 | } 48 | 49 | [TestMethod] 50 | public void Encode_Negative() 51 | { 52 | ExceptionAssert.Throws(() => Varint.Encode(-1)); 53 | } 54 | 55 | [TestMethod] 56 | public void TooBig_Int32() 57 | { 58 | var bytes = Varint.Encode((long)Int32.MaxValue + 1); 59 | ExceptionAssert.Throws(() => Varint.DecodeInt32(bytes)); 60 | } 61 | 62 | [TestMethod] 63 | public void TooBig_Int64() 64 | { 65 | var bytes = "ffffffffffffffffff7f".ToHexBuffer(); 66 | ExceptionAssert.Throws(() => Varint.DecodeInt64(bytes)); 67 | } 68 | 69 | [TestMethod] 70 | public void Unterminated() 71 | { 72 | var bytes = "ff".ToHexBuffer(); 73 | ExceptionAssert.Throws(() => Varint.DecodeInt64(bytes)); 74 | } 75 | 76 | [TestMethod] 77 | public void Empty() 78 | { 79 | var bytes = new byte[0]; 80 | ExceptionAssert.Throws(() => Varint.DecodeInt64(bytes)); 81 | } 82 | 83 | [TestMethod] 84 | public async Task WriteAsync() 85 | { 86 | using (var ms = new MemoryStream()) 87 | { 88 | await ms.WriteVarintAsync(long.MaxValue); 89 | ms.Position = 0; 90 | Assert.AreEqual(long.MaxValue, ms.ReadVarint64()); 91 | } 92 | } 93 | 94 | [TestMethod] 95 | public void WriteAsync_Negative() 96 | { 97 | var ms = new MemoryStream(); 98 | ExceptionAssert.Throws(() => ms.WriteVarintAsync(-1).Wait()); 99 | } 100 | 101 | [TestMethod] 102 | public void WriteAsync_Cancel() 103 | { 104 | var ms = new MemoryStream(); 105 | var cs = new CancellationTokenSource(); 106 | cs.Cancel(); 107 | ExceptionAssert.Throws(() => ms.WriteVarintAsync(0, cs.Token).Wait()); 108 | } 109 | 110 | [TestMethod] 111 | public async Task ReadAsync() 112 | { 113 | using (var ms = new MemoryStream("ffffffffffffffff7f".ToHexBuffer())) 114 | { 115 | var v = await ms.ReadVarint64Async(); 116 | Assert.AreEqual(long.MaxValue, v); 117 | } 118 | } 119 | 120 | [TestMethod] 121 | public void ReadAsync_Cancel() 122 | { 123 | var ms = new MemoryStream(new byte[] { 0 }); 124 | var cs = new CancellationTokenSource(); 125 | cs.Cancel(); 126 | ExceptionAssert.Throws(() => ms.ReadVarint32Async(cs.Token).Wait()); 127 | } 128 | 129 | [TestMethod] 130 | public void Example() 131 | { 132 | for (long v = 1; v <= 0xFFFFFFFL; v = v << 4) 133 | { 134 | Console.Write($"| {v} (0x{v.ToString("x")}) "); 135 | Console.WriteLine($"| {Varint.Encode(v).ToHexString()} |"); 136 | } 137 | } 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /test/PeerTest.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Text; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | using Microsoft.VisualStudio.TestTools.UnitTesting; 6 | using System.IO; 7 | 8 | namespace Ipfs 9 | { 10 | 11 | [TestClass] 12 | public class PeerTest 13 | { 14 | const string marsId = "QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; 15 | const string plutoId = "QmSoLPppuBtQSGwKDZT2M73ULpjvfd3aZ6ha4oFGL1KrGM"; 16 | const string marsPublicKey = "CAASogEwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGBAKGUtbRQf+a9SBHFEruNAUatS/tsGUnHuCtifGrlbYPELD3UyyhWf/FYczBCavx3i8hIPEW2jQv4ehxQxi/cg9SHswZCQblSi0ucwTBFr8d40JEiyB9CcapiMdFQxdMgGvXEOQdLz1pz+UPUDojkdKZq8qkkeiBn7KlAoGEocnmpAgMBAAE="; 17 | static string marsAddress = "/ip4/10.1.10.10/tcp/29087/ipfs/QmSoLMeWqB7YGVLJN3pNLQpmmEk35v6wYtsMGLzSr5QBU3"; 18 | 19 | [TestMethod] 20 | public new void ToString() 21 | { 22 | Assert.AreEqual("", new Peer().ToString()); 23 | Assert.AreEqual(marsId, new Peer { Id = marsId }.ToString()); 24 | } 25 | 26 | [TestMethod] 27 | public void DefaultValues() 28 | { 29 | var peer = new Peer(); 30 | Assert.AreEqual(null, peer.Id); 31 | Assert.AreEqual(0, peer.Addresses.Count()); 32 | Assert.AreEqual("unknown/0.0", peer.ProtocolVersion); 33 | Assert.AreEqual("unknown/0.0", peer.AgentVersion); 34 | Assert.AreEqual(null, peer.PublicKey); 35 | Assert.AreEqual(false, peer.IsValid()); // missing peer ID 36 | Assert.AreEqual(null, peer.ConnectedAddress); 37 | Assert.IsFalse(peer.Latency.HasValue); 38 | } 39 | 40 | [TestMethod] 41 | public void ConnectedPeer() 42 | { 43 | var peer = new Peer 44 | { 45 | ConnectedAddress = new MultiAddress(marsAddress), 46 | Latency = TimeSpan.FromHours(3.03 * 2) 47 | }; 48 | Assert.AreEqual(marsAddress, peer.ConnectedAddress.ToString()); 49 | Assert.AreEqual(3.03 * 2, peer.Latency.Value.TotalHours); 50 | } 51 | 52 | [TestMethod] 53 | public void Validation_No_Id() 54 | { 55 | var peer = new Peer(); 56 | Assert.AreEqual(false, peer.IsValid()); 57 | } 58 | 59 | [TestMethod] 60 | public void Validation_With_Id() 61 | { 62 | Peer peer = marsId; 63 | Assert.AreEqual(true, peer.IsValid()); 64 | } 65 | 66 | [TestMethod] 67 | public void Validation_With_Id_Pubkey() 68 | { 69 | var peer = new Peer 70 | { 71 | Id = marsId, 72 | PublicKey = marsPublicKey 73 | }; 74 | Assert.AreEqual(true, peer.IsValid()); 75 | } 76 | 77 | [TestMethod] 78 | public void Validation_With_Id_Invalid_Pubkey() 79 | { 80 | var peer = new Peer 81 | { 82 | Id = plutoId, 83 | PublicKey = marsPublicKey 84 | }; 85 | Assert.AreEqual(false, peer.IsValid()); 86 | } 87 | 88 | [TestMethod] 89 | public void Value_Equality() 90 | { 91 | var a0 = new Peer { Id = marsId }; 92 | var a1 = new Peer { Id = marsId }; 93 | var b = new Peer { Id = plutoId }; 94 | Peer c = null; 95 | Peer d = null; 96 | 97 | Assert.IsTrue(c == d); 98 | Assert.IsFalse(c == b); 99 | Assert.IsFalse(b == c); 100 | 101 | Assert.IsFalse(c != d); 102 | Assert.IsTrue(c != b); 103 | Assert.IsTrue(b != c); 104 | 105 | #pragma warning disable 1718 106 | Assert.IsTrue(a0 == a0); 107 | Assert.IsTrue(a0 == a1); 108 | Assert.IsFalse(a0 == b); 109 | 110 | #pragma warning disable 1718 111 | Assert.IsFalse(a0 != a0); 112 | Assert.IsFalse(a0 != a1); 113 | Assert.IsTrue(a0 != b); 114 | 115 | Assert.IsTrue(a0.Equals(a0)); 116 | Assert.IsTrue(a0.Equals(a1)); 117 | Assert.IsFalse(a0.Equals(b)); 118 | 119 | Assert.AreEqual(a0, a0); 120 | Assert.AreEqual(a0, a1); 121 | Assert.AreNotEqual(a0, b); 122 | 123 | Assert.AreEqual(a0, a0); 124 | Assert.AreEqual(a0, a1); 125 | Assert.AreNotEqual(a0, b); 126 | 127 | Assert.AreEqual(a0.GetHashCode(), a0.GetHashCode()); 128 | Assert.AreEqual(a0.GetHashCode(), a1.GetHashCode()); 129 | Assert.AreNotEqual(a0.GetHashCode(), b.GetHashCode()); 130 | } 131 | 132 | 133 | [TestMethod] 134 | public void Implicit_Conversion_From_String() 135 | { 136 | Peer a = marsId; 137 | Assert.IsInstanceOfType(a, typeof(Peer)); 138 | } 139 | 140 | } 141 | } 142 | 143 | -------------------------------------------------------------------------------- /src/CoreApi/ICoreApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs.CoreApi 6 | { 7 | /// 8 | /// The IPFS Core API. 9 | /// 10 | /// 11 | /// The Core API defines a set of interfaces to manage IPFS. 12 | /// 13 | /// 14 | public interface ICoreApi 15 | { 16 | /// 17 | /// Provides access to the Bitswap API. 18 | /// 19 | /// 20 | /// An object that implements . 21 | /// 22 | IBitswapApi Bitswap { get; } 23 | 24 | /// 25 | /// Provides access to the Block API. 26 | /// 27 | /// 28 | /// An object that implements . 29 | /// 30 | IBlockApi Block { get; } 31 | 32 | /// 33 | /// Provides access to the Block Repository API. 34 | /// 35 | /// 36 | /// An object that implements . 37 | /// 38 | IBlockRepositoryApi BlockRepository { get; } 39 | 40 | /// 41 | /// Provides access to the Bootstrap API. 42 | /// 43 | /// 44 | /// An object that implements . 45 | /// 46 | IBootstrapApi Bootstrap { get; } 47 | 48 | /// 49 | /// Provides access to the Config API. 50 | /// 51 | /// 52 | /// An object that implements . 53 | /// 54 | IConfigApi Config { get; } 55 | 56 | /// 57 | /// Provides access to the Dag API. 58 | /// 59 | /// 60 | /// An object that implements . 61 | /// 62 | IDagApi Dag { get; } 63 | 64 | /// 65 | /// Provides access to the DHT API. 66 | /// 67 | /// 68 | /// An object that implements . 69 | /// 70 | IDhtApi Dht { get; } 71 | 72 | /// 73 | /// Provides access to the DNS API. 74 | /// 75 | /// 76 | /// An object that implements . 77 | /// 78 | IDnsApi Dns { get; } 79 | 80 | /// 81 | /// Provides access to the File System API. 82 | /// 83 | /// 84 | /// An object that implements . 85 | /// 86 | IFileSystemApi FileSystem { get; } 87 | 88 | /// 89 | /// Provides access to the Generic API. 90 | /// 91 | /// 92 | /// An object that implements . 93 | /// 94 | IGenericApi Generic { get; } 95 | 96 | /// 97 | /// Provides access to the Key API. 98 | /// 99 | /// 100 | /// An object that implements . 101 | /// 102 | IKeyApi Key { get; } 103 | 104 | /// 105 | /// Provides access to the Name API. 106 | /// 107 | /// 108 | /// An object that implements . 109 | /// 110 | INameApi Name { get; } 111 | 112 | /// 113 | /// Provides access to the Object API. 114 | /// 115 | /// 116 | /// An object that implements . 117 | /// 118 | IObjectApi Object { get; } 119 | 120 | /// 121 | /// Provides access to the Pin API. 122 | /// 123 | /// 124 | /// An object that implements . 125 | /// 126 | IPinApi Pin { get; } 127 | 128 | /// 129 | /// Provides access to the PubSub API. 130 | /// 131 | /// 132 | /// An object that implements . 133 | /// 134 | IPubSubApi PubSub { get; } 135 | 136 | /// 137 | /// Provides access to the Stats (statistics) API. 138 | /// 139 | /// 140 | /// An object that implements . 141 | /// 142 | IStatsApi Stats { get; } 143 | 144 | /// 145 | /// Provides access to the Swarm API. 146 | /// 147 | /// 148 | /// An object that implements . 149 | /// 150 | ISwarmApi Swarm { get; } 151 | } 152 | } 153 | -------------------------------------------------------------------------------- /src/CoreApi/IPubSubApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Text; 5 | using System.Threading; 6 | using System.Threading.Tasks; 7 | 8 | namespace Ipfs.CoreApi 9 | { 10 | /// 11 | /// Allows you to publish messages to a given topic, and also to 12 | /// subscribe to new messages on a given topic. 13 | /// 14 | /// 15 | /// 16 | /// This is an experimental feature. It is not intended in its current state 17 | /// to be used in a production environment. 18 | /// 19 | /// 20 | /// To use, the daemon must be run with '--enable-pubsub-experiment'. 21 | /// 22 | /// 23 | /// Pubsub API spec 24 | public interface IPubSubApi 25 | { 26 | /// 27 | /// Get the subscribed topics. 28 | /// 29 | /// 30 | /// Is used to stop the task. When cancelled, the is raised. 31 | /// 32 | /// 33 | /// A task that represents the asynchronous operation. The task's value is 34 | /// a sequence of for each topic. 35 | /// 36 | Task> SubscribedTopicsAsync(CancellationToken cancel = default(CancellationToken)); 37 | 38 | /// 39 | /// Get the peers that are pubsubing with us. 40 | /// 41 | /// 42 | /// When specified, only peers subscribing on the topic are returned. 43 | /// 44 | /// 45 | /// Is used to stop the task. When cancelled, the is raised. 46 | /// 47 | /// 48 | /// A task that represents the asynchronous operation. The task's value is 49 | /// a sequence of . 50 | /// 51 | Task> PeersAsync(string topic = null, CancellationToken cancel = default(CancellationToken)); 52 | 53 | /// 54 | /// Publish a string message to a given topic. 55 | /// 56 | /// 57 | /// The topic name. 58 | /// 59 | /// 60 | /// The message to publish. 61 | /// 62 | /// 63 | /// Is used to stop the task. When cancelled, the is raised. 64 | /// 65 | /// 66 | /// A task that represents the asynchronous operation. 67 | /// 68 | Task PublishAsync(string topic, string message, CancellationToken cancel = default(CancellationToken)); 69 | 70 | /// 71 | /// Publish a binary message to a given topic. 72 | /// 73 | /// 74 | /// The topic name. 75 | /// 76 | /// 77 | /// The message to publish. 78 | /// 79 | /// 80 | /// Is used to stop the task. When cancelled, the is raised. 81 | /// 82 | /// 83 | /// A task that represents the asynchronous operation. 84 | /// 85 | Task PublishAsync(string topic, byte[] message, CancellationToken cancel = default(CancellationToken)); 86 | 87 | /// 88 | /// Publish a binary message to a given topic. 89 | /// 90 | /// 91 | /// The topic name. 92 | /// 93 | /// 94 | /// The message to publish. 95 | /// 96 | /// 97 | /// Is used to stop the task. When cancelled, the is raised. 98 | /// 99 | /// 100 | /// A task that represents the asynchronous operation. 101 | /// 102 | Task PublishAsync(string topic, Stream message, CancellationToken cancel = default(CancellationToken)); 103 | 104 | /// 105 | /// Subscribe to messages on a given topic. 106 | /// 107 | /// 108 | /// The topic name. 109 | /// 110 | /// 111 | /// The action to perform when a is received. 112 | /// 113 | /// 114 | /// Is used to stop the topic listener. When cancelled, the 115 | /// is NOT raised. 116 | /// 117 | /// 118 | /// A task that represents the asynchronous operation. 119 | /// 120 | /// 121 | /// The is invoked on the topic listener thread. 122 | /// 123 | Task SubscribeAsync(string topic, Action handler, CancellationToken cancellationToken); 124 | 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/DagLink.cs: -------------------------------------------------------------------------------- 1 | using Google.Protobuf; 2 | using System; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Linq; 6 | using System.Text; 7 | using System.Threading.Tasks; 8 | 9 | namespace Ipfs 10 | { 11 | 12 | /// 13 | /// A link to another node in the IPFS Merkle DAG. 14 | /// 15 | public class DagLink : IMerkleLink 16 | { 17 | /// 18 | /// Create a new instance of class. 19 | /// 20 | /// The name associated with the linked node. 21 | /// The of the linked node. 22 | /// The serialised size (in bytes) of the linked node. 23 | public DagLink(string name, Cid id, long size) 24 | { 25 | this.Name = name; 26 | this.Id = id; 27 | this.Size = size; 28 | } 29 | 30 | /// 31 | /// Creates a new instance of the class from the 32 | /// specified . 33 | /// 34 | /// 35 | /// Some type of a Merkle link. 36 | /// 37 | public DagLink(IMerkleLink link) 38 | { 39 | this.Name = link.Name; 40 | this.Id = link.Id; 41 | this.Size = link.Size; 42 | } 43 | 44 | /// 45 | /// Creates a new instance of the class from the 46 | /// specified . 47 | /// 48 | /// 49 | /// A containing the binary representation of the 50 | /// DagLink. 51 | /// 52 | public DagLink(Stream stream) 53 | { 54 | Read(stream); 55 | } 56 | 57 | /// 58 | /// Creates a new instance of the class from the 59 | /// specified . 60 | /// 61 | /// ( 62 | /// A containing the binary representation of the 63 | /// DagLink. 64 | /// 65 | public DagLink(CodedInputStream stream) 66 | { 67 | Read(stream); 68 | } 69 | 70 | /// 71 | public string Name { get; private set; } 72 | 73 | /// 74 | public Cid Id { get; private set; } 75 | 76 | /// 77 | public long Size { get; private set; } 78 | 79 | /// 80 | /// Writes the binary representation of the link to the specified . 81 | /// 82 | /// 83 | /// The to write to. 84 | /// 85 | public void Write(Stream stream) 86 | { 87 | using (var cos = new CodedOutputStream(stream, true)) 88 | { 89 | Write(cos); 90 | } 91 | } 92 | 93 | /// 94 | /// Writes the binary representation of the link to the specified . 95 | /// 96 | /// 97 | /// The to write to. 98 | /// 99 | public void Write(CodedOutputStream stream) 100 | { 101 | if (stream == null) 102 | throw new ArgumentNullException("stream"); 103 | 104 | stream.WriteTag(1, WireFormat.WireType.LengthDelimited); 105 | Id.Write(stream); 106 | 107 | if (Name != null) 108 | { 109 | stream.WriteTag(2, WireFormat.WireType.LengthDelimited); 110 | stream.WriteString(Name); 111 | } 112 | 113 | stream.WriteTag(3, WireFormat.WireType.Varint); 114 | stream.WriteInt64(Size); 115 | } 116 | 117 | void Read(Stream stream) 118 | { 119 | using (var cis = new CodedInputStream(stream, true)) 120 | { 121 | Read(cis); 122 | } 123 | } 124 | 125 | void Read(CodedInputStream stream) 126 | { 127 | while (!stream.IsAtEnd) 128 | { 129 | var tag = stream.ReadTag(); 130 | switch (WireFormat.GetTagFieldNumber(tag)) 131 | { 132 | case 1: 133 | Id = Cid.Read(stream); 134 | break; 135 | case 2: 136 | Name = stream.ReadString(); 137 | break; 138 | case 3: 139 | Size = stream.ReadInt64(); 140 | break; 141 | default: 142 | throw new InvalidDataException("Unknown field number"); 143 | } 144 | } 145 | } 146 | 147 | /// 148 | /// Returns the IPFS binary representation as a byte array. 149 | /// 150 | /// 151 | /// A byte array. 152 | /// 153 | public byte[] ToArray() 154 | { 155 | using (var ms = new MemoryStream()) 156 | { 157 | Write(ms); 158 | return ms.ToArray(); 159 | } 160 | } 161 | 162 | } 163 | } -------------------------------------------------------------------------------- /src/CoreApi/IKeyApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.CoreApi 8 | { 9 | /// 10 | /// Manages cryptographic keys. 11 | /// 12 | /// 13 | /// 14 | /// The Key API is work in progress! There be dragons here. 15 | /// 16 | /// 17 | /// Key API spec 18 | public interface IKeyApi 19 | { 20 | /// 21 | /// Creates a new key. 22 | /// 23 | /// 24 | /// The local name of the key. 25 | /// 26 | /// 27 | /// The type of key to create; "rsa" or "ed25519". 28 | /// 29 | /// 30 | /// The size, in bits, of the key. 31 | /// 32 | /// 33 | /// Is used to stop the task. When cancelled, the is raised. 34 | /// 35 | /// 36 | /// A task that represents the asynchronous operation. The task's result is 37 | /// the key that was created. 38 | /// 39 | Task CreateAsync( 40 | string name, 41 | string keyType, 42 | int size, 43 | CancellationToken cancel = default(CancellationToken)); 44 | 45 | /// 46 | /// List all the keys. 47 | /// 48 | /// 49 | /// Is used to stop the task. When cancelled, the is raised. 50 | /// 51 | /// 52 | /// A task that represents the asynchronous operation. The task's result is 53 | /// a sequence of IPFS keys. 54 | /// 55 | Task> ListAsync(CancellationToken cancel = default(CancellationToken)); 56 | 57 | /// 58 | /// Delete the specified key. 59 | /// 60 | /// 61 | /// The local name of the key. 62 | /// 63 | /// 64 | /// Is used to stop the task. When cancelled, the is raised. 65 | /// 66 | /// 67 | /// A task that represents the asynchronous operation. The task's result is 68 | /// the key that was deleted. 69 | /// 70 | Task RemoveAsync(string name, CancellationToken cancel = default(CancellationToken)); 71 | 72 | /// 73 | /// Rename the specified key. 74 | /// 75 | /// 76 | /// The local name of the key. 77 | /// 78 | /// 79 | /// The new local name of the key. 80 | /// 81 | /// 82 | /// Is used to stop the task. When cancelled, the is raised. 83 | /// 84 | /// 85 | /// A task that represents the asynchronous operation. The task's result is 86 | /// a sequence of IPFS keys that were renamed. 87 | /// 88 | Task RenameAsync(string oldName, string newName, CancellationToken cancel = default(CancellationToken)); 89 | 90 | /// 91 | /// Export a key to a PEM encoded password protected PKCS #8 container. 92 | /// 93 | /// 94 | /// The local name of the key. 95 | /// 96 | /// 97 | /// The PEM's password. 98 | /// 99 | /// 100 | /// Is used to stop the task. When cancelled, the is raised. 101 | /// 102 | /// 103 | /// A task that represents the asynchronous operation. The task's result is 104 | /// the password protected PEM string. 105 | /// 106 | Task ExportAsync(string name, char[] password, CancellationToken cancel = default(CancellationToken)); 107 | 108 | /// 109 | /// Import a key from a PEM encoded password protected PKCS #8 container. 110 | /// 111 | /// 112 | /// The local name of the key. 113 | /// 114 | /// 115 | /// The PEM encoded PKCS #8 container. 116 | /// 117 | /// 118 | /// The 's password. 119 | /// 120 | /// 121 | /// Is used to stop the task. When cancelled, the is raised. 122 | /// 123 | /// 124 | /// A task that represents the asynchronous operation. The task's result 125 | /// is the newly imported key. 126 | /// 127 | Task ImportAsync(string name, string pem, char[] password = null, CancellationToken cancel = default(CancellationToken)); 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/HexString.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | using System.Text; 5 | using System.IO; 6 | 7 | namespace Ipfs 8 | { 9 | /// 10 | /// A codec for Hexadecimal. 11 | /// 12 | /// 13 | /// 14 | /// A codec for a hexadecimal string, and . Adds the extension method 15 | /// to encode a byte array and to decode a hexadecimal . 16 | /// 17 | /// 18 | public static class HexString 19 | { 20 | static readonly string[] LowerCaseHexStrings = 21 | Enumerable.Range(byte.MinValue, byte.MaxValue + 1) 22 | .Select(v => v.ToString("x2")) 23 | .ToArray(); 24 | static readonly string[] UpperCaseHexStrings = 25 | Enumerable.Range(byte.MinValue, byte.MaxValue + 1) 26 | .Select(v => v.ToString("X2")) 27 | .ToArray(); 28 | static readonly Dictionary HexBytes = 29 | Enumerable.Range(byte.MinValue, byte.MaxValue + 1) 30 | .SelectMany(v => new [] { 31 | new { Value = v, String = v.ToString("x2") } , 32 | new { Value = v, String = v.ToString("X2") } 33 | } ) 34 | .Distinct() 35 | .ToDictionary(v => v.String, v => (byte) v.Value); 36 | 37 | 38 | /// 39 | /// Converts an array of 8-bit unsigned integers to its equivalent hexadecimal string representation. 40 | /// 41 | /// 42 | /// An array of 8-bit unsigned integers. 43 | /// 44 | /// 45 | /// One of the format specifiers ("G" and "x" for lower-case hex digits, or "X" for the upper-case). 46 | /// The default is "G". 47 | /// 48 | /// 49 | /// The string representation, in hexadecimal, of the contents of . 50 | /// 51 | public static string Encode(byte[] buffer, string format = "G") 52 | { 53 | string[] hexStrings; 54 | switch (format) 55 | { 56 | case "G": 57 | case "x": 58 | hexStrings = LowerCaseHexStrings; 59 | break; 60 | case "X": 61 | hexStrings = UpperCaseHexStrings; 62 | break; 63 | default: 64 | throw new FormatException(string.Format("Invalid HexString format '{0}', only 'G', 'x' or 'X' are allowed.", format)); 65 | } 66 | 67 | StringBuilder s = new StringBuilder(buffer.Length * 2); 68 | foreach (var v in buffer) 69 | s.Append(hexStrings[v]); 70 | return s.ToString(); 71 | } 72 | 73 | /// 74 | /// Converts an array of 8-bit unsigned integers to its equivalent hexadecimal string representation. 75 | /// 76 | /// 77 | /// An array of 8-bit unsigned integers. 78 | /// 79 | /// 80 | /// One of the format specifiers ("G" and "x" for lower-case hex digits, or "X" for the upper-case). 81 | /// The default is "G". 82 | /// 83 | /// 84 | /// The string representation, in hexadecimal, of the contents of . 85 | /// 86 | public static string ToHexString(this byte[] buffer, string format = "G") 87 | { 88 | return Encode(buffer, format); 89 | } 90 | 91 | /// 92 | /// Converts the specified , which encodes binary data as hexadecimal digits, 93 | /// to an equivalent 8-bit unsigned integer array. 94 | /// 95 | /// 96 | /// The hexadecimal string to convert. 97 | /// 98 | /// 99 | /// An array of 8-bit unsigned integers that is equivalent to . 100 | /// 101 | public static byte[] Decode(string s) 102 | { 103 | int n = s.Length; 104 | if (n % 2 != 0) 105 | throw new InvalidDataException("The hex string length must be a multiple of 2."); 106 | 107 | var buffer = new byte[n / 2]; 108 | for (int i = 0, j = 0; i < n; i += 2, j++) 109 | { 110 | var hex = s.Substring(i, 2); 111 | byte value; 112 | if (!HexBytes.TryGetValue(hex, out value)) 113 | throw new InvalidDataException(string.Format("'{0}' is not a valid hexadecimal byte.", hex)); 114 | buffer[j] = value; 115 | } 116 | 117 | return buffer; 118 | } 119 | 120 | /// 121 | /// Converts the specified , which encodes binary data as a hexadecimal string, 122 | /// to an equivalent 8-bit unsigned integer array. 123 | /// 124 | /// 125 | /// The hexadecimal string to convert. 126 | /// 127 | /// 128 | /// An array of 8-bit unsigned integers that is equivalent to . 129 | /// 130 | public static byte[] ToHexBuffer(this string s) 131 | { 132 | return Decode(s); 133 | } 134 | } 135 | } 136 | -------------------------------------------------------------------------------- /src/CoreApi/ISwarmApi.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | using System.Threading; 5 | using System.Threading.Tasks; 6 | 7 | namespace Ipfs.CoreApi 8 | { 9 | /// 10 | /// Manages the swarm of peers. 11 | /// 12 | /// 13 | /// The swarm is a sequence of connected peer nodes. 14 | /// 15 | /// Swarm API spec 16 | public interface ISwarmApi 17 | { 18 | /// 19 | /// Get the peers in the current swarm. 20 | /// 21 | /// 22 | /// Is used to stop the task. When cancelled, the is raised. 23 | /// 24 | /// 25 | /// A task that represents the asynchronous operation. The task's value 26 | /// is a sequence of peer nodes. 27 | /// 28 | Task> AddressesAsync(CancellationToken cancel = default(CancellationToken)); 29 | 30 | /// 31 | /// Get the peers that are connected to this node. 32 | /// 33 | /// 34 | /// Is used to stop the task. When cancelled, the is raised. 35 | /// 36 | /// 37 | /// A task that represents the asynchronous operation. The task's value 38 | /// is a sequence of Connected Peers. 39 | /// 40 | Task> PeersAsync(CancellationToken cancel = default(CancellationToken)); 41 | 42 | /// 43 | /// Connect to a peer. 44 | /// 45 | /// 46 | /// An ipfs , such as 47 | /// /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ. 48 | /// 49 | /// 50 | /// Is used to stop the task. When cancelled, the is raised. 51 | /// 52 | Task ConnectAsync(MultiAddress address, CancellationToken cancel = default(CancellationToken)); 53 | 54 | /// 55 | /// Disconnect from a peer. 56 | /// 57 | /// 58 | /// An ipfs , such as 59 | /// /ip4/104.131.131.82/tcp/4001/ipfs/QmaCpDMGvV2BGHeYERUEnRQAwe3N8SzbUtfsmvsqQLuvuJ. 60 | /// 61 | /// 62 | /// Is used to stop the task. When cancelled, the is raised. 63 | /// 64 | Task DisconnectAsync(MultiAddress address, CancellationToken cancel = default(CancellationToken)); 65 | 66 | /// 67 | /// Adds a new address filter. 68 | /// 69 | /// 70 | /// An allowed address. For example "/ip4/104.131.131.82" or 71 | /// "/ip4/192.168.0.0/ipcidr/16". 72 | /// 73 | /// 74 | /// If true the filter will persist across daemon reboots. 75 | /// 76 | /// 77 | /// Is used to stop the task. When cancelled, the is raised. 78 | /// 79 | /// 80 | /// A task that represents the asynchronous operation. The task's result is 81 | /// the address filter that was added. 82 | /// 83 | /// 84 | Task AddAddressFilterAsync(MultiAddress address, bool persist = false, CancellationToken cancel = default(CancellationToken)); 85 | 86 | /// 87 | /// List all the address filters. 88 | /// 89 | /// 90 | /// If true only persisted filters are listed. 91 | /// 92 | /// 93 | /// Is used to stop the task. When cancelled, the is raised. 94 | /// 95 | /// 96 | /// A task that represents the asynchronous operation. The task's result is 97 | /// a sequence of addresses filters. 98 | /// 99 | /// 100 | Task> ListAddressFiltersAsync(bool persist = false, CancellationToken cancel = default(CancellationToken)); 101 | 102 | /// 103 | /// Delete the specified address filter. 104 | /// 105 | /// 106 | /// For example "/ip4/104.131.131.82" or 107 | /// "/ip4/192.168.0.0/ipcidr/16". 108 | /// 109 | /// 110 | /// If true the filter is also removed from the persistent store. 111 | /// 112 | /// 113 | /// Is used to stop the task. When cancelled, the is raised. 114 | /// 115 | /// 116 | /// A task that represents the asynchronous operation. The task's result is 117 | /// the address filter that was removed. 118 | /// 119 | /// 120 | Task RemoveAddressFilterAsync(MultiAddress address, bool persist = false, CancellationToken cancel = default(CancellationToken)); 121 | 122 | } 123 | } 124 | -------------------------------------------------------------------------------- /src/Registry/Codec.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Text; 4 | 5 | namespace Ipfs.Registry 6 | { 7 | /// 8 | /// Metadata for IPFS multi-codec. 9 | /// 10 | /// 11 | /// IPFS assigns a unique and to codecs. 12 | /// See table.csv 13 | /// for the currently defined multi-codecs. 14 | /// 15 | /// 16 | public class Codec 17 | { 18 | internal static Dictionary Names = new Dictionary(); 19 | internal static Dictionary Codes = new Dictionary(); 20 | 21 | /// 22 | /// Register the standard multi-codecs for IPFS. 23 | /// 24 | /// 25 | static Codec() 26 | { 27 | Register("raw", 0x55); 28 | Register("cms", 0x57); // Not official yet, https://github.com/multiformats/multicodec/pull/69 29 | Register("cbor", 0x51); 30 | Register("protobuf", 0x50); 31 | Register("rlp", 0x60); 32 | Register("bencode", 0x63); 33 | Register("multicodec", 0x30); 34 | Register("multihash", 0x31); 35 | Register("multiaddr", 0x32); 36 | Register("multibase", 0x33); 37 | Register("dag-pb", 0x70); 38 | Register("dag-cbor", 0x71); 39 | Register("git-raw", 0x78); 40 | Register("eth-block", 0x90); 41 | Register("eth-block-list", 0x91); 42 | Register("eth-tx-trie", 0x92); 43 | Register("eth-tx", 0x93); 44 | Register("eth-tx-receipt-trie", 0x94); 45 | Register("eth-tx-receipt", 0x95); 46 | Register("eth-state-trie", 0x96); 47 | Register("eth-account-snapshot", 0x97); 48 | Register("eth-storage-trie", 0x98); 49 | Register("bitcoin-block", 0xb0); 50 | Register("bitcoin-tx", 0xb1); 51 | Register("zcash-block", 0xc0); 52 | Register("zcash-tx", 0xc1); 53 | Register("stellar-block", 0xd0); 54 | Register("stellar-tx", 0xd1); 55 | Register("torrent-info", 0x7b); 56 | Register("torrent-file", 0x7c); 57 | Register("ed25519-pub", 0xed); 58 | } 59 | 60 | /// 61 | /// The name of the codec. 62 | /// 63 | /// 64 | /// A unique name. 65 | /// 66 | public string Name { get; private set; } 67 | 68 | /// 69 | /// The IPFS code assigned to the codec. 70 | /// 71 | /// 72 | /// Valid codes at . 73 | /// 74 | public int Code { get; private set; } 75 | 76 | /// 77 | /// Use to create a new instance of a . 78 | /// 79 | Codec() 80 | { 81 | } 82 | 83 | /// 84 | /// The of the codec. 85 | /// 86 | /// 87 | /// The of the codec. 88 | /// 89 | public override string ToString() 90 | { 91 | return Name; 92 | } 93 | 94 | /// 95 | /// Register a new IPFS codec. 96 | /// 97 | /// 98 | /// The name of the codec. 99 | /// 100 | /// 101 | /// The IPFS code assigned to the codec. 102 | /// 103 | /// 104 | /// A new . 105 | /// 106 | /// 107 | /// When the or is already defined. 108 | /// 109 | /// 110 | /// When the is null or empty. 111 | /// 112 | public static Codec Register(string name, int code) 113 | { 114 | if (string.IsNullOrWhiteSpace(name)) 115 | throw new ArgumentNullException("name"); 116 | if (Names.ContainsKey(name)) 117 | throw new ArgumentException(string.Format("The IPFS codec name '{0}' is already defined.", name)); 118 | if (Codes.ContainsKey(code)) 119 | throw new ArgumentException(string.Format("The IPFS codec code '{0}' is already defined.", code)); 120 | 121 | var a = new Codec 122 | { 123 | Name = name, 124 | Code = code 125 | }; 126 | Names[name] = a; 127 | Codes[code] = a; 128 | 129 | return a; 130 | } 131 | /// 132 | /// Remove an IPFS codec from the registry. 133 | /// 134 | /// 135 | /// The to remove. 136 | /// 137 | public static void Deregister(Codec codec) 138 | { 139 | Names.Remove(codec.Name); 140 | Codes.Remove(codec.Code); 141 | } 142 | 143 | /// 144 | /// A sequence consisting of all codecs. 145 | /// 146 | /// 147 | /// All the registered codecs. 148 | /// 149 | public static IEnumerable All 150 | { 151 | get { return Names.Values; } 152 | } 153 | 154 | } 155 | } 156 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # net-ipfs-core 2 | 3 | [![build status](https://ci.appveyor.com/api/projects/status/github/richardschneider/net-ipfs-core?branch=master&svg=true)](https://ci.appveyor.com/project/richardschneider/net-ipfs-core) 4 | [![travis build](https://travis-ci.org/richardschneider/net-ipfs-core.svg?branch=master)](https://travis-ci.org/richardschneider/net-ipfs-core) 5 | [![Codacy Badge](https://api.codacy.com/project/badge/Grade/a911c5ae1f044e92bfc006bbc945e135)](https://www.codacy.com/app/makaretu/net-ipfs-core?utm_source=github.com&utm_medium=referral&utm_content=richardschneider/net-ipfs-core&utm_campaign=Badge_Grade) 6 | [![Coverage Status](https://coveralls.io/repos/richardschneider/net-ipfs-core/badge.svg?branch=master&service=github)](https://coveralls.io/github/richardschneider/net-ipfs-core?branch=master) 7 | [![Version](https://img.shields.io/nuget/v/Ipfs.Core.svg)](https://www.nuget.org/packages/Ipfs.Core) 8 | [![docs](https://cdn.rawgit.com/richardschneider/net-ipfs-core/master/doc/images/docs-latest-green.svg)](https://richardschneider.github.io/net-ipfs-core/articles/intro.html) 9 | 10 | The core objects and interfaces of the [IPFS](https://github.com/ipfs/ipfs) (Inter Planetary File System) for .Net (C#, VB, F# etc.) 11 | 12 | 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. 13 | 14 | It supports the following runtimes 15 | - .NET Framework 4.5 16 | - .NET Standard 1.4 17 | - .NET Standard 2.0 18 | 19 | More information is on the [Documentation](https://richardschneider.github.io/net-ipfs-core/) web site. 20 | 21 | ## Getting started 22 | 23 | Published releases of IPFS Core are available on [NuGet](https://www.nuget.org/packages/ipfs.core/). To install, run the following command in the [Package Manager Console](https://docs.nuget.org/docs/start-here/using-the-package-manager-console). 24 | 25 | PM> Install-Package Ipfs.Core 26 | 27 | Or using [dotnet](https://docs.microsoft.com/en-us/dotnet/core/tools/dotnet?tabs=netcore21) 28 | 29 | > dotnet add package Ipfs.Core 30 | 31 | 32 | For the latest build or older non-released builds see [Continuous Integration](https://github.com/richardschneider/net-ipfs-core/wiki/Continuous-Integration). 33 | 34 | ## Major objects 35 | 36 | - [MerkleDag](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.DagNode.html) 37 | - [MultiAddress](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.MultiAddress.html) 38 | - [MultiHash](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.MultiHash.html) 39 | 40 | See the [API Documentation](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.html) for a list of all objects. 41 | 42 | ### MultiHash 43 | 44 | All hashes in IPFS are encoded with [multihash](https://github.com/multiformats/multihash), a self-describing hash format. The actual hash function used depends on security requirements. The cryptosystem of IPFS is upgradeable, meaning that as hash functions are broken, networks can shift to stronger hashes. There is no free lunch, as objects may need to be rehashed, or links duplicated. But ensuring that tools built do not assume a pre-defined length of hash digest means tools that work with today's hash functions will also work with tomorrows longer hash functions too. 45 | 46 | ### MultiAddress 47 | 48 | A standard way to represent a networks address that supports [multiple network protocols](https://github.com/multiformats/multiaddr). It is represented as a series of tuples, a protocol code and an optional value. For example, an IPFS file at a sepcific address over ipv4 and tcp is 49 | 50 | /ip4/10.1.10.10/tcp/80/ipfs/QmVcSqVEsvm5RR9mBLjwpb2XjFVn5bPdPL69mL8PH45pPC 51 | 52 | ### Merkle DAG 53 | 54 | The [DagNode](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.DagNode.html) is a directed acyclic graph whose edges are a 55 | [DagLink](https://richardschneider.github.io/net-ipfs-core/api/Ipfs.DagLink.html). This means that links to objects can authenticate 56 | the objects themselves, and that every object contains a secure 57 | representation of its children. 58 | 59 | Every Merkle is a directed acyclic graph (DAG) because each node is accessed via its name (the hash of `DagNode`). Each branch of Merkle is the hash of its local content (data and links); naming children by their hash instead of their full contents. So after creation there is no way to edit a DagNode. This prevents cycles (assuming there are no hash collisions) since one can not link the first created node to the last note to create the last reference. 60 | 61 | ## Base58 62 | 63 | Most binary data (objects) in IPFS is represented as a [Base-58](https://en.wikipedia.org/wiki/Base58) string; the BitCoin alphabet is used. 64 | 65 | > Base58 is a group of binary-to-text encoding schemes used to represent large integers as alphanumeric text. It is similar to Base64 but has been modified to avoid both non-alphanumeric characters and letters which might look ambiguous when printed. It is therefore designed for human users who manually enter the data, copying from some visual source, but also allows easy copy and paste because a double-click will usually select the whole string. 66 | 67 | ## Related Projects 68 | 69 | - [IPFS DSL](https://github.com/cloveekprojeqt/ipfs-dsl) - A declarative embedded language for building compositional programs and protocols over the InterPlanetary File System. 70 | - [IPFS HTTP Client](https://github.com/richardschneider/net-ipfs-http-client) - A .Net client library for the IPFS HTTP API. 71 | - [IPFS HTTP Gateway](https://github.com/richardschneider/net-ipfs-http-gateway) - Serves IPFS files/directories via HTTP. 72 | - [IPFS Engine](https://github.com/richardschneider/net-ipfs-engine) - Implements the Core API. 73 | - [Peer Talk](https://github.com/richardschneider/peer-talk) - Peer to peer communication. 74 | 75 | ## License 76 | Copyright © 2015-2019 Richard Schneider (makaretu@gmail.com) 77 | 78 | The IPFS Core 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-core/blob/master/LICENSE) file for more information. 79 | 80 | Buy Me A Coffee 81 | -------------------------------------------------------------------------------- /src/Cryptography/Keccak.cs: -------------------------------------------------------------------------------- 1 | /* 2 | * The package was named SHA3 it is really Keccak 3 | * https://medium.com/@ConsenSys/are-you-really-using-sha-3-or-old-code-c5df31ad2b0 4 | * See 5 | * 6 | * The SHA3 package doesn't create .Net Standard package. 7 | * This is a copy of https://bitbucket.org/jdluzen/sha3/raw/d1fd55dc225d18a7fb61515b62d3c8f164d2e788/SHA3/SHA3.cs 8 | */ 9 | using System; 10 | using System.Collections.Generic; 11 | using System.Linq; 12 | using System.Text; 13 | 14 | namespace Ipfs.Cryptography 15 | { 16 | internal abstract class Keccak : 17 | #if PORTABLE 18 | IHashAlgorithm 19 | #else 20 | System.Security.Cryptography.HashAlgorithm 21 | #endif 22 | { 23 | 24 | #region Implementation 25 | public const int KeccakB = 1600; 26 | public const int KeccakNumberOfRounds = 24; 27 | public const int KeccakLaneSizeInBits = 8 * 8; 28 | 29 | public readonly ulong[] RoundConstants; 30 | 31 | protected ulong[] state; 32 | protected byte[] buffer; 33 | protected int buffLength; 34 | #if PORTABLE || NETSTANDARD14 35 | protected byte[] HashValue; 36 | protected int HashSizeValue; 37 | #endif 38 | protected int keccakR; 39 | 40 | public int KeccakR 41 | { 42 | get 43 | { 44 | return keccakR; 45 | } 46 | protected set 47 | { 48 | keccakR = value; 49 | } 50 | } 51 | 52 | public int SizeInBytes 53 | { 54 | get 55 | { 56 | return KeccakR / 8; 57 | } 58 | } 59 | 60 | public int HashByteLength 61 | { 62 | get 63 | { 64 | return HashSizeValue / 8; 65 | } 66 | } 67 | 68 | public 69 | #if (!PORTABLE && !NETSTANDARD14) 70 | override 71 | #endif 72 | bool CanReuseTransform 73 | { 74 | get 75 | { 76 | return true; 77 | } 78 | } 79 | 80 | protected Keccak(int hashBitLength) 81 | { 82 | if (hashBitLength != 224 && hashBitLength != 256 && hashBitLength != 384 && hashBitLength != 512) 83 | throw new ArgumentException("hashBitLength must be 224, 256, 384, or 512", "hashBitLength"); 84 | Initialize(); 85 | HashSizeValue = hashBitLength; 86 | switch (hashBitLength) 87 | { 88 | case 224: 89 | KeccakR = 1152; 90 | break; 91 | case 256: 92 | KeccakR = 1088; 93 | break; 94 | case 384: 95 | KeccakR = 832; 96 | break; 97 | case 512: 98 | KeccakR = 576; 99 | break; 100 | } 101 | RoundConstants = new ulong[] 102 | { 103 | 0x0000000000000001UL, 104 | 0x0000000000008082UL, 105 | 0x800000000000808aUL, 106 | 0x8000000080008000UL, 107 | 0x000000000000808bUL, 108 | 0x0000000080000001UL, 109 | 0x8000000080008081UL, 110 | 0x8000000000008009UL, 111 | 0x000000000000008aUL, 112 | 0x0000000000000088UL, 113 | 0x0000000080008009UL, 114 | 0x000000008000000aUL, 115 | 0x000000008000808bUL, 116 | 0x800000000000008bUL, 117 | 0x8000000000008089UL, 118 | 0x8000000000008003UL, 119 | 0x8000000000008002UL, 120 | 0x8000000000000080UL, 121 | 0x000000000000800aUL, 122 | 0x800000008000000aUL, 123 | 0x8000000080008081UL, 124 | 0x8000000000008080UL, 125 | 0x0000000080000001UL, 126 | 0x8000000080008008UL 127 | }; 128 | } 129 | 130 | protected ulong ROL(ulong a, int offset) 131 | { 132 | return (((a) << ((offset) % KeccakLaneSizeInBits)) ^ ((a) >> (KeccakLaneSizeInBits - ((offset) % KeccakLaneSizeInBits)))); 133 | } 134 | 135 | protected void AddToBuffer(byte[] array, ref int offset, ref int count) 136 | { 137 | int amount = Math.Min(count, buffer.Length - buffLength); 138 | Buffer.BlockCopy(array, offset, buffer, buffLength, amount); 139 | offset += amount; 140 | buffLength += amount; 141 | count -= amount; 142 | } 143 | 144 | public 145 | #if !PORTABLE && !NETSTANDARD14 146 | override 147 | #endif 148 | byte[] Hash 149 | { 150 | get 151 | { 152 | return HashValue; 153 | } 154 | } 155 | 156 | public 157 | #if !PORTABLE 158 | override 159 | #endif 160 | int HashSize 161 | { 162 | get 163 | { 164 | return HashSizeValue; 165 | } 166 | } 167 | 168 | #endregion 169 | 170 | public 171 | #if !PORTABLE 172 | override 173 | #endif 174 | void Initialize() 175 | { 176 | buffLength = 0; 177 | state = new ulong[5 * 5];//1600 bits 178 | HashValue = null; 179 | } 180 | 181 | protected 182 | #if !PORTABLE 183 | override 184 | #endif 185 | void HashCore(byte[] array, int ibStart, int cbSize) 186 | { 187 | if (array == null) 188 | throw new ArgumentNullException("array"); 189 | if (ibStart < 0) 190 | throw new ArgumentOutOfRangeException("ibStart"); 191 | if (cbSize > array.Length) 192 | throw new ArgumentOutOfRangeException("cbSize"); 193 | if (ibStart + cbSize > array.Length) 194 | throw new ArgumentOutOfRangeException("ibStart or cbSize"); 195 | } 196 | 197 | #if PORTABLE 198 | public abstract int TransformBlock(byte[] inputBuffer, int inputOffset, int inputCount, byte[] outputBuffer, int outputOffset); 199 | public abstract byte[] TransformFinalBlock(byte[] inputBuffer, int inputOffset, int inputCount); 200 | #endif 201 | } 202 | } --------------------------------------------------------------------------------