public void Want_Cancel() { var bitSwap = new BitSwapService { SwarmService = new SwarmService { LocalPeer = _self } }; var cid = new DagNode(Encoding.UTF8.GetBytes("BitswapTest unknown block")).Id; var cancel = new CancellationTokenSource(); var task = bitSwap.WantAsync(cid, _self.Id, cancel.Token); bitSwap.PeerWants(_self.Id).ToArray().Should().Contain(cid); cancel.Cancel(); Assert.True(task.IsCanceled); bitSwap.PeerWants(_self.Id).ToArray().Should().NotContain(cid); }
public async Task Get_Nonexistent() { var data = Encoding.UTF8.GetBytes("Some data for net-ipfs-api-test that cannot be found"); var node = new DagNode(data); var id = node.Id; var cs = new CancellationTokenSource(500); try { var _ = await ipfs.Object.GetAsync(id, cs.Token); Assert.Fail("Did not throw TaskCanceledException"); } catch (TaskCanceledException) { return; } }
private async Task <ObjectLinkDetailDto> Put(IFormFile file, string inputenc = "json", string datafieldenc = "text", bool pin = false) { if (datafieldenc != "text") // TODO { throw new NotImplementedException("Only datafieldenc = `text` is allowed."); } IDagNode node; switch (inputenc) { case "protobuf": await using (var stream = file.OpenReadStream()) { var dag = new DagNode(stream); node = await DfsService.ObjectApi.PutAsync(dag, Cancel); } break; case "json": // TODO default: throw new ArgumentException("inputenc", $"Input encoding '{inputenc}' is not supported."); } if (pin) { await DfsService.PinApi.AddAsync(node.Id, false, Cancel); } return(new ObjectLinkDetailDto { Hash = node.Id, Links = node.Links.Select(link => new ObjectLinkDto { Hash = link.Id, Name = link.Name, Size = link.Size }) }); }
public async Task Unwant() { var block = new DagNode(Encoding.UTF8.GetBytes("BitswapApiTest unknown block 2")); #pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed Task.Run(() => ipfs.Bitswap.GetAsync(block.Id).Wait()); var endTime = DateTime.Now.AddSeconds(10); while (true) { if (DateTime.Now > endTime) { Assert.Fail("wanted block is missing"); } await Task.Delay(100); var wants = await ipfs.Bitswap.WantsAsync(); if (wants.Contains(block.Id)) { break; } } await ipfs.Bitswap.UnwantAsync(block.Id); endTime = DateTime.Now.AddSeconds(10); while (true) { if (DateTime.Now > endTime) { Assert.Fail("unwanted block is present"); } await Task.Delay(100); var wants = await ipfs.Bitswap.WantsAsync(); if (!wants.Contains(block.Id)) { break; } } }
/// <inheritdoc /> public CBORObject Deserialise(byte[] data) { using (var ms = new MemoryStream(data, false)) { var node = new DagNode(ms); var links = node.Links .Select(link => CBORObject.NewMap() .Add("Cid", CBORObject.NewMap() .Add("/", link.Id.Encode()) ) .Add("Name", link.Name) .Add("Size", link.Size)) .ToArray(); var cbor = CBORObject.NewMap() .Add("data", node.DataBytes) .Add("links", links); return(cbor); } }
public void WantList() { var bitswap = new Bitswap { Swarm = new Swarm { LocalPeer = self } }; Assert.AreEqual(0, bitswap.PeerWants(self.Id).Count()); var cid = new DagNode(Encoding.UTF8.GetBytes("BitswapTest unknown block")).Id; var cancel = new CancellationTokenSource(); var task = bitswap.Want(cid, self.Id, cancel.Token); CollectionAssert.Contains(bitswap.PeerWants(self.Id).ToArray(), cid); bitswap.Unwant(cid); CollectionAssert.DoesNotContain(bitswap.PeerWants(self.Id).ToArray(), cid); }
public void WantList() { var bitSwap = new BitSwapService { SwarmService = new SwarmService { LocalPeer = _self } }; Assert.AreEqual(0, bitSwap.PeerWants(_self.Id).Count()); var cid = new DagNode(Encoding.UTF8.GetBytes("BitswapTest unknown block")).Id; var cancel = new CancellationTokenSource(); var _ = bitSwap.WantAsync(cid, _self.Id, cancel.Token); bitSwap.PeerWants(_self.Id).ToArray().Should().Contain(cid); bitSwap.Unwant(cid); bitSwap.PeerWants(_self.Id).ToArray().Should().NotContain(cid); }
public async Task UnWant() { await _dfsService.StartAsync(); try { var cts = new CancellationTokenSource(); var block = new DagNode(Encoding.UTF8.GetBytes("BitSwapApiTest unknown block 2")); var wantTask = _dfsService.BitSwapApi.GetAsync(block.Id, cts.Token).ConfigureAwait(false); var endTime = DateTime.Now.AddSeconds(10); while (true) { if (DateTime.Now > endTime) { throw new Exception("wanted block is missing"); } await Task.Delay(100, cts.Token); var w = await _dfsService.BitSwapApi.WantsAsync(cancel : cts.Token); if (w.Contains(block.Id)) { break; } } cts.Cancel(); _dfsService.BitSwapApi.UnWant(block.Id, cts.Token); var wants = await _dfsService.BitSwapApi.WantsAsync(cancel : cts.Token); wants.ToArray().Should().NotContain(block.Id); //Race condition as wantTask will be on another thread and could create unpredictable behaviour //Assert.True(wantTask.IsCanceled); } finally { await _dfsService.StopAsync(); } }
public async Task GetBlock_Timeout() { var block = new DagNode(Encoding.UTF8.GetBytes("BitSwapApiTest unknown block")); await _dfsService.StartAsync(); try { var cts = new CancellationTokenSource(300); ExceptionAssert.Throws <TaskCanceledException>(() => { var _ = _dfsService.BitSwapApi.GetAsync(block.Id, cts.Token).Result; }); Assert.AreEqual(0, (await _dfsService.BitSwapApi.WantsAsync(cancel: cts.Token)).Count()); } finally { await _dfsService.StopAsync(); } }
/// <summary> /// Creates a new instance of the <see cref="ChunkedStream"/> class with /// the specified <see cref="IBlockApi"/> and <see cref="DagNode"/>. /// </summary> /// <param name="blockService"></param> /// <param name="keyChain"></param> /// <param name="dag"></param> public ChunkedStream(IBlockApi blockService, KeyChain keyChain, DagNode dag) { BlockService = blockService; KeyChain = keyChain; var links = dag.Links.ToArray(); var dm = Serializer.Deserialize <DataMessage>(dag.DataStream); fileSize = (long)dm.FileSize; ulong position = 0; for (int i = 0; i < dm.BlockSizes.Length; ++i) { blocks.Add(new BlockInfo { Id = links[i].Id, Position = (long)position }); position += dm.BlockSizes[i]; } }
public void LinksAreSorted() { var a = Encoding.UTF8.GetBytes("a"); var anode = new DagNode(a); var alink = anode.ToLink("a"); var b = Encoding.UTF8.GetBytes("b"); var bnode = new DagNode(b); var blink = bnode.ToLink("b"); var ab = Encoding.UTF8.GetBytes("ab"); var node = new DagNode(ab, new[] { blink, alink }); Assert.AreEqual(ab, node.DataBytes); Assert.AreEqual(2, node.Links.Count()); Assert.AreEqual("Qma5sYpEc9hSYdkuXpMDJYem95Mj7hbEd9C412dEQ4ZkfP", node.Id.ToString()); }
public async Task <Stream> GetAsync(string path, bool compress = false, CancellationToken cancel = default(CancellationToken)) { var cid = await ipfs.ResolveIpfsPathToCidAsync(path, cancel).ConfigureAwait(false); var ms = new MemoryStream(); if (compress) { using (var tarStream = new TarOutputStream(ms, 1)) using (var archive = TarArchive.CreateOutputTarArchive(tarStream)) { archive.IsStreamOwner = false; await AddTarNodeAsync(cid, cid.Encode(), tarStream, cancel).ConfigureAwait(false); } } else { var block = await ipfs.Block.GetAsync(cid, cancel).ConfigureAwait(false); var dm = new DataMessage { Type = DataType.Raw }; DagNode dag = null; if (cid.ContentType == "dag-pb") { dag = new DagNode(block.DataStream); dm = Serializer.Deserialize <DataMessage>(dag.DataStream); } if (dm.Type != DataType.Directory) { await AddNodeAsync(cid, ms, cancel).ConfigureAwait(false); } else { throw new ArgumentException("Can't put a folder in a stream. Consider using compressing"); } } ms.Position = 0; return(ms); }
public void Found_Count() { var bitswap = new Bitswap { Swarm = new Swarm { LocalPeer = self } }; var a = new DagNode(Encoding.UTF8.GetBytes("BitswapTest found block a")); Assert.AreEqual(0, bitswap.Found(a)); var cancel = new CancellationTokenSource(); var task1 = bitswap.Want(a.Id, self.Id, cancel.Token); var task2 = bitswap.Want(a.Id, self.Id, cancel.Token); Assert.AreEqual(2, bitswap.Found(a)); Assert.IsTrue(task1.IsCompleted); Assert.IsTrue(task2.IsCompleted); }
public void MultipleLinksOnlyDag() { var a = Encoding.UTF8.GetBytes("a"); var anode = new DagNode(a); var alink = anode.ToLink("a"); var b = Encoding.UTF8.GetBytes("b"); var bnode = new DagNode(b); var blink = bnode.ToLink("b"); var node = new DagNode(null, new[] { alink, blink }); Assert.AreEqual(0, node.DataBytes.Length); Assert.AreEqual(2, node.Links.Count()); Assert.AreEqual("QmbNgNPPykP4YTuAeSa3DsnBJWLVxccrqLUZDPNQfizGKs", node.Id.ToString()); RoundtripTest(node); }
public async Task <IFileSystemNode> ListFileAsync(string path, CancellationToken cancel = default(CancellationToken)) { var cid = await ipfs.ResolveIpfsPathToCidAsync(path, cancel); var block = await ipfs.Block.GetAsync(cid, cancel); var dag = new DagNode(block.DataStream); var dm = Serializer.Deserialize <DataMessage>(dag.DataStream); var fsn = new FileSystemNode { Id = cid, Links = dag.Links .Select(l => new FileSystemLink { Id = l.Id, Name = l.Name, Size = l.Size }) .ToArray(), IsDirectory = dm.Type == DataType.Directory, Size = (long)(dm.FileSize ?? 0) }; // Cannot determine if a link points to a directory. The link's block must be // read to get this info. if (fsn.IsDirectory) { foreach (FileSystemLink link in fsn.Links) { var lblock = await ipfs.Block.GetAsync(link.Id, cancel); var ldag = new DagNode(lblock.DataStream); var ldm = Serializer.Deserialize <DataMessage>(ldag.DataStream); link.IsDirectory = ldm.Type == DataType.Directory; } } return(fsn); }
async Task AddNodeAsync(Cid cid, MemoryStream ms, CancellationToken cancel) { var block = await ipfs.Block.GetAsync(cid, cancel).ConfigureAwait(false); var dm = new DataMessage { Type = DataType.Raw }; DagNode dag = null; if (cid.ContentType == "dag-pb") { dag = new DagNode(block.DataStream); dm = Serializer.Deserialize <DataMessage>(dag.DataStream); } if (dm.Type != DataType.Directory) { var content = await ReadFileAsync(cid, cancel).ConfigureAwait(false); await content.CopyToAsync(ms); } }
public async Task Unwant() { await ipfs.StartAsync(); try { var block = new DagNode(Encoding.UTF8.GetBytes("BitswapApiTest unknown block 2")); Task wantTask = ipfs.Bitswap.GetAsync(block.Id); var endTime = DateTime.Now.AddSeconds(10); while (true) { if (DateTime.Now > endTime) { Assert.Fail("wanted block is missing"); } await Task.Delay(100); var w = await ipfs.Bitswap.WantsAsync(); if (w.Contains(block.Id)) { break; } } await ipfs.Bitswap.UnwantAsync(block.Id); var wants = await ipfs.Bitswap.WantsAsync(); CollectionAssert.DoesNotContain(wants.ToArray(), block.Id); Assert.IsTrue(wantTask.IsCanceled); } finally { await ipfs.StopAsync(); } }
async Task <FileSystemNode> BuildTreeNodeAsync( IEnumerable <FileSystemNode> nodes, AddFileOptions options, CancellationToken cancel) { var blockService = GetBlockService(options); // Build the DAG that contains all the file nodes. var links = nodes.Select(n => n.ToLink()).ToArray(); var fileSize = (ulong)nodes.Sum(n => n.Size); var dagSize = nodes.Sum(n => n.DagSize); var dm = new DataMessage { Type = DataType.File, FileSize = fileSize, BlockSizes = nodes.Select(n => (ulong)n.Size).ToArray() }; var pb = new MemoryStream(); ProtoBuf.Serializer.Serialize <DataMessage>(pb, dm); var dag = new DagNode(pb.ToArray(), links, options.Hash); // Save it. dag.Id = await blockService.PutAsync( data : dag.ToArray(), multiHash : options.Hash, encoding : options.Encoding, pin : options.Pin, cancel : cancel).ConfigureAwait(false); return(new FileSystemNode { Id = dag.Id, Size = (long)dm.FileSize, DagSize = dagSize + dag.Size, Links = links }); }
private DagNetConfig Convert2DagNetConfig(NetworkDiscovery map) { DagNetConfig dagNetConfig = new DagNetConfig(); dagNetConfig.ReplicationPort = (int)this.ReplicationPort; dagNetConfig.NetworkCompression = NetworkManager.MapESEReplNetworkOption(this.NetworkCompression); dagNetConfig.NetworkEncryption = NetworkManager.MapESEReplNetworkOption(this.NetworkEncryption); foreach (LogicalNetwork logicalNetwork in map.LogicalNetworks) { DagNetwork dagNetwork = new DagNetwork(); dagNetwork.Name = logicalNetwork.Name; dagNetwork.Description = logicalNetwork.Description; dagNetwork.ReplicationEnabled = logicalNetwork.ReplicationEnabled; dagNetwork.IsDnsMapped = logicalNetwork.HasDnsNic(); foreach (Subnet subnet in logicalNetwork.Subnets) { dagNetwork.Subnets.Add(subnet.SubnetId.ToString()); } dagNetConfig.Networks.Add(dagNetwork); } foreach (ClusterNode clusterNode in map.Nodes) { DagNode dagNode = new DagNode(); dagNode.Name = clusterNode.Name.NetbiosName; foreach (ClusterNic clusterNic in clusterNode.Nics) { if (clusterNic.IPAddress != null) { DagNode.Nic nic = new DagNode.Nic(); nic.IpAddress = clusterNic.IPAddress.ToString(); nic.NetworkName = clusterNic.ClusterNetwork.LogicalNetwork.Name; dagNode.Nics.Add(nic); } } dagNetConfig.Nodes.Add(dagNode); } return dagNetConfig; }
public void RemoveLink() { var a = Encoding.UTF8.GetBytes("a"); var anode = new DagNode(a); var b = Encoding.UTF8.GetBytes("b"); var bnode = new DagNode(b); var c = Encoding.UTF8.GetBytes("c"); var cnode = new DagNode(c, new[] { anode.ToLink(), bnode.ToLink() }); var dnode = cnode.RemoveLink(anode.ToLink()); Assert.False(ReferenceEquals(dnode, cnode)); Assert.AreEqual(1, dnode.DataBytes.Length); Assert.AreEqual(1, dnode.Links.Count()); Assert.AreEqual(bnode.Id.ToString(), dnode.Links.First().Id.ToString()); Assert.AreEqual(bnode.Size, dnode.Links.First().Size); RoundtripTest(cnode); }
public static Nonced <InnerType> Mine <InnerType>(InnerType value, int zerosInFront, CancellationToken cancellationToken) { // TODO: This is a total no-no for competitive mining. long ourNonce = 1337; for (;;) // the cookie monster loop is the most delicious infinite loop { if (cancellationToken != null && cancellationToken.IsCancellationRequested) { return(null); } Nonced <InnerType> attempt = new Nonced <InnerType>(value, ourNonce); DagNode attemptedNode = IpfsDagSerialization.MapToDag <Nonced <InnerType> >(attempt); bool match = FoundGoldenNonce(Base58.Decode(attemptedNode.Hash), zerosInFront); if (match) { return(attempt); } ourNonce++; } }
private static DagNode MapToDag(Type objectType, object value) { if (value == null) { return(new DagNode(new byte[] {})); } if (ConverterRepository.ContainsKey(objectType)) { return(ConverterRepository[objectType](value)); } List <IMerkleLink> links = new List <IMerkleLink>(); // Okay, we need to create a converter. foreach (var property in objectType.GetProperties(BindingFlags.Public | BindingFlags.Instance)) { var property_value = property.GetValue(value); DagNode property_node = MapToDag(property.PropertyType, property_value); links.Add(property_node.ToLink(property.Name)); } return(new DagNode(null, links)); }
/// <summary> /// Performs the chunking. /// </summary> /// <param name="stream"> /// The data source. /// </param> /// <param name="name"> /// A name for the data. /// </param> /// <param name="options"> /// The options when adding data to the IPFS file system. /// </param> /// <param name="blockService"> /// The destination for the chunked data block(s). /// </param> /// <param name="keyChain"> /// Used to protect the chunked data blocks(s). /// </param> /// <param name="cancel"> /// Is used to stop the task. When cancelled, the <see cref="TaskCanceledException" /> is raised. /// </param> /// <returns> /// A task that represents the asynchronous operation. The task's value is /// the sequence of file system nodes of the added data blocks. /// </returns> public async Task <List <UnixFsNode> > ChunkAsync(Stream stream, string name, AddFileOptions options, IBlockApi blockService, IKeyApi keyChain, CancellationToken cancel) { var protecting = !string.IsNullOrWhiteSpace(options.ProtectionKey); var nodes = new List <UnixFsNode>(); var chunkSize = options.ChunkSize; var chunk = new byte[chunkSize]; var chunking = true; var totalBytes = 0UL; while (chunking) { // Get an entire chunk. var length = 0; while (length < chunkSize) { var n = await stream.ReadAsync(chunk, length, chunkSize - length, cancel).ConfigureAwait(false); if (n < 1) { chunking = false; break; } length += n; totalBytes += (uint)n; } // Only generate empty block, when the stream is empty. if (length != 0 || nodes.Count <= 0) { options.Progress?.Report(new TransferProgress { Name = name, Bytes = totalBytes }); // if protected data, then get CMS structure. if (protecting) { // TODO: Inefficent to copy chunk, use ArraySegment in DataMessage.Data var plain = new byte[length]; Array.Copy(chunk, plain, length); var cipher = await keyChain.CreateProtectedDataAsync(options.ProtectionKey, plain, cancel) .ConfigureAwait(false); var cid = await blockService.PutAsync( cipher, "cms", options.Hash, options.Encoding, options.Pin, cancel).ConfigureAwait(false); nodes.Add(new UnixFsNode { Id = cid, Size = length, DagSize = cipher.Length, Links = UnixFsLink.None }); } else if (options.RawLeaves) { // TODO: Inefficent to copy chunk, use ArraySegment in DataMessage.Data var data = new byte[length]; Array.Copy(chunk, data, length); var cid = await blockService.PutAsync( data, "raw", options.Hash, options.Encoding, options.Pin, cancel).ConfigureAwait(false); nodes.Add(new UnixFsNode { Id = cid, Size = length, DagSize = length, Links = UnixFsLink.None }); } else { // Build the DAG. var dm = new DataMessage { Type = DataType.File, FileSize = (ulong)length }; if (length > 0) { // TODO: Inefficent to copy chunk, use ArraySegment in DataMessage.Data var data = new byte[length]; Array.Copy(chunk, data, length); dm.Data = data; } var pb = new MemoryStream(); Serializer.Serialize(pb, dm); var dag = new DagNode(pb.ToArray(), null, options.Hash); // Save it. dag.Id = await blockService.PutAsync( dag.ToArray(), multiHash : options.Hash, encoding : options.Encoding, pin : options.Pin, cancel : cancel).ConfigureAwait(false); var node = new UnixFsNode { Id = dag.Id, Size = length, DagSize = dag.Size, Links = UnixFsLink.None }; nodes.Add(node); } } } return(nodes); }
public async Task <IFileSystemNode> AddAsync( Stream stream, string name, AddFileOptions options, CancellationToken cancel) { options = options ?? new AddFileOptions(); // TODO: various options if (options.Trickle) { throw new NotImplementedException("Trickle"); } var blockService = GetBlockService(options); var keyChain = await ipfs.KeyChain(cancel).ConfigureAwait(false); var chunker = new SizeChunker(); var nodes = await chunker.ChunkAsync(stream, name, options, blockService, keyChain, cancel).ConfigureAwait(false); // Multiple nodes for the file? FileSystemNode node = null; if (nodes.Count() == 1) { node = nodes.First(); } else { // Build the DAG that contains all the file nodes. var links = nodes.Select(n => n.ToLink()).ToArray(); var fileSize = (ulong)nodes.Sum(n => n.Size); var dm = new DataMessage { Type = DataType.File, FileSize = fileSize, BlockSizes = nodes.Select(n => (ulong)n.Size).ToArray() }; var pb = new MemoryStream(); ProtoBuf.Serializer.Serialize <DataMessage>(pb, dm); var dag = new DagNode(pb.ToArray(), links, options.Hash); // Save it. dag.Id = await blockService.PutAsync( data : dag.ToArray(), multiHash : options.Hash, encoding : options.Encoding, pin : options.Pin, cancel : cancel).ConfigureAwait(false); node = new FileSystemNode { Id = dag.Id, Size = (long)dm.FileSize, DagSize = dag.Size, Links = links }; } // Wrap in directory? if (options.Wrap) { var link = node.ToLink(name); var wlinks = new IFileSystemLink[] { link }; node = await CreateDirectoryAsync(wlinks, options, cancel).ConfigureAwait(false); } else { node.Name = name; } // Advertise the root node. if (options.Pin && ipfs.IsStarted) { await ipfs.Dht.ProvideAsync(node.Id, advertise : true, cancel : cancel).ConfigureAwait(false); } // Return the file system node. return(node); }
public async Task <IFileSystemNode> ListFileAsync(string path, CancellationToken cancel = default(CancellationToken)) { var cid = await ipfs.ResolveIpfsPathToCidAsync(path, cancel).ConfigureAwait(false); var block = await ipfs.Block.GetAsync(cid, cancel).ConfigureAwait(false); // TODO: A content-type registry should be used. if (cid.ContentType == "dag-pb") { // fall thru } else if (cid.ContentType == "raw") { return(new FileSystemNode { Id = cid, Size = block.Size }); } else if (cid.ContentType == "cms") { return(new FileSystemNode { Id = cid, Size = block.Size }); } else { throw new NotSupportedException($"Cannot read content type '{cid.ContentType}'."); } var dag = new DagNode(block.DataStream); var dm = Serializer.Deserialize <DataMessage>(dag.DataStream); var fsn = new FileSystemNode { Id = cid, Links = dag.Links .Select(l => new FileSystemLink { Id = l.Id, Name = l.Name, Size = l.Size }) .ToArray(), IsDirectory = dm.Type == DataType.Directory, Size = (long)(dm.FileSize ?? 0) }; // Cannot determine if a link points to a directory. The link's block must be // read to get this info. if (fsn.IsDirectory) { foreach (FileSystemLink link in fsn.Links) { var lblock = await ipfs.Block.GetAsync(link.Id, cancel).ConfigureAwait(false); var ldag = new DagNode(lblock.DataStream); var ldm = Serializer.Deserialize <DataMessage>(ldag.DataStream); link.IsDirectory = ldm.Type == DataType.Directory; } } return(fsn); }
public async Task <DagNode> PutAsync(DagNode node, CancellationToken cancel = default(CancellationToken)) { node.Id = await ipfs.Block.PutAsync(node.ToArray(), cancel : cancel); return(node); }
public Task <DagNode> PutAsync(byte[] data, IEnumerable <IMerkleLink> links = null, CancellationToken cancel = default(CancellationToken)) { var node = new DagNode(data, links); return(PutAsync(node, cancel)); }
public async Task Remove_Unknown() { var dag = new DagNode(Encoding.UTF8.GetBytes("some unknown info for net-ipfs-engine-pin-test")); await _dfs.PinApi.RemoveAsync(dag.Id); }
public async Task <DagNode> PutAsync(DagNode node, CancellationToken cancel = default(CancellationToken)) { var json = await ipfs.UploadAsync("object/put", cancel, node.ToArray(), "inputenc=protobuf"); return(node); }
public async Task Remove_Unknown() { var ipfs = TestFixture.Ipfs; var dag = new DagNode(Encoding.UTF8.GetBytes("some unknown info for net-ipfs-engine-pin-test")); await ipfs.Pin.RemoveAsync(dag.Id, true); }