2 /* -----------------------------------------------------------------------
3 * <copyright file="TreeHash.cs" company="GRNet">
5 * Copyright 2011-2012 GRNET S.A. All rights reserved.
7 * Redistribution and use in source and binary forms, with or
8 * without modification, are permitted provided that the following
11 * 1. Redistributions of source code must retain the above
12 * copyright notice, this list of conditions and the following
15 * 2. Redistributions in binary form must reproduce the above
16 * copyright notice, this list of conditions and the following
17 * disclaimer in the documentation and/or other materials
18 * provided with the distribution.
21 * THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
22 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
23 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
24 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
25 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
26 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
27 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
28 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
29 * AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 * ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 * POSSIBILITY OF SUCH DAMAGE.
34 * The views and conclusions contained in the software and
35 * documentation are those of the authors and should not be
36 * interpreted as representing official policies, either expressed
37 * or implied, of GRNET S.A.
39 * -----------------------------------------------------------------------
43 using System.Collections.Concurrent;
44 using System.Collections.Generic;
45 using System.Diagnostics.Contracts;
48 using System.Threading.Tasks;
51 using Newtonsoft.Json;
52 using Newtonsoft.Json.Linq;
54 namespace Pithos.Network
58 private const string DEFAULT_HASH_ALGORITHM = "sha256";
59 private const long DEFAULT_BLOCK_SIZE = 4*1024*1024;
60 public string BlockHash { get; set; }
61 public long BlockSize { get; set; }
68 Contract.Ensures(Contract.Result<long>() >= 0);
74 throw new ArgumentOutOfRangeException("Bytes");
75 Contract.Requires(value >= 0);
76 Contract.EndContractBlock();
84 public Guid FileId { get; set; }
86 private readonly Lazy<byte[]> _topHash;
89 get { return _topHash.Value; }
92 private IList<byte[]> _hashes=new List<byte[]>();
94 public IList<byte[]> Hashes
96 get { return _hashes; }
104 [ContractInvariantMethod]
105 private void Invariants()
107 Contract.Invariant(_bytes>=0);
111 public TreeHash(string algorithm)
113 BlockHash = algorithm;
114 _topHash = new Lazy<byte[]>(() =>
117 //Cast the hashes to an IList or create a new list out of the hashes
118 var hashMap = (_hashes as IList<byte[]>)??_hashes.ToList();
119 return Signature.CalculateTopHash(hashMap, BlockHash);
123 //Returns a Json representation of the hashes, as required by Pithos
124 public string ToJson()
126 var value = new JObject();
127 //We avoid using JObject's dynamic features because they use exceptions to detect new properties.
128 value["block_hash"] = BlockHash;
129 //Create a string array for all the hashes
131 string[] hashes=null ;
133 hashes= GetHashesAsStrings();
134 value["hashes"]= new JArray(hashes);
135 value["block_size"] = BlockSize;
136 value["bytes"] = Bytes;
138 var json = JsonConvert.SerializeObject(value, Formatting.None);
142 private string[] _stringHashes;
143 //Returns the hashes as an array of hash strings. Used for serialization to Json
144 public string[] GetHashesAsStrings()
147 ?? (_stringHashes = Hashes.Select(hash => hash.ToHashString()).ToArray());
150 ConcurrentDictionary<string, int> _blocks;
151 //Retruns the hashes as a dictionary to the block location. Used to locate blocks
152 public IDictionary<string,int> HashDictionary
156 Func<ConcurrentDictionary<string, int>> blocksInit = () =>
159 new ConcurrentDictionary<string, int>();
164 foreach (var hash in this.Hashes)
166 blocks[hash.ToHashString()] = blockIndex++;
171 return _blocks ?? (_blocks = blocksInit());
175 //Saves the Json representation to a file
176 public async Task Save(string filePath)
178 if (String.IsNullOrWhiteSpace(filePath))
179 throw new ArgumentNullException("filePath");
181 var fileName = FileId.ToString("N");
182 var path = Path.Combine(filePath, fileName);
183 if (!Directory.Exists(filePath))
184 Directory.CreateDirectory(filePath);
186 var json=await TaskEx.Run(()=>ToJson());
187 await FileAsync.WriteAllText(path, json);
190 public static async Task<TreeHash> LoadTreeHash(string dataPath,Guid fileId)
192 var fileName = fileId.ToString("N");
193 var path = Path.Combine(dataPath, fileName);
195 var json=await FileAsync.ReadAllText(path);
196 var treeHash = Parse(json);
197 treeHash.FileId = fileId;
201 public static TreeHash Empty = new TreeHash(DEFAULT_HASH_ALGORITHM)
203 BlockSize = DEFAULT_BLOCK_SIZE,
207 //Parse a json string and return a TreeHash
208 //Returns an empty TreeHash if the string is null or empty
209 public static TreeHash Parse(string json)
211 if (String.IsNullOrWhiteSpace(json))
214 var value = JsonConvert.DeserializeObject<JObject>(json);
216 throw new ArgumentException("The json parameter doesn't contain any json data","json");
217 Contract.Assume(value!=null);
219 var blockHash = (string) value["block_hash"];
220 var size = value.Value<int>("block_size");
221 var bytes = value.Value<long>("bytes");
222 var hashes = value.Value<JArray>("hashes");
223 var hashValues = from JToken token in hashes
224 select token.Value<string>().ToBytes();
226 var treeHash = new TreeHash(blockHash)
229 Hashes = hashValues.ToList(),