/// <summary> /// Marshals an entry into a byte[] to be sent to restAPI /// </summary> /// <param name="e">Entry to be marshaled</param> /// <returns>Marshaled entry</returns> public static byte[] MarshalBinary(DataStructs.EntryData e) { var entryBStruct = new List <byte>(); var idsSize = MarshalExtIDsSize(e); idsSize = Bytes.CheckEndian(idsSize); // Header // 1 byte version byte version = 0; entryBStruct.Add(version); // 32 byte chainid var chain = e.ChainId; entryBStruct.AddRange(chain); // Ext Ids Size entryBStruct.AddRange(idsSize); // Payload // ExtIDS if (e.ExtIDs != null) { var ids = MarshalExtIDsBinary(e); entryBStruct.AddRange(ids); } // Content var content = e.Content; entryBStruct.AddRange(content); return(entryBStruct.ToArray()); }
/// <summary> /// Constructs a new EntryData object /// </summary> /// <param name="content">Content of Entry (message pack)</param> /// <param name="extIds">Unique Ids used for first entry of chain to construct a unique chain ID</param> /// <param name="chainId">ChainID of chain</param> /// <returns>EntryData object</returns> public static DataStructs.EntryData NewEntry(byte[] content, byte[][] extIds, byte[] chainId) { var entry = new DataStructs.EntryData(); entry.Content = content; entry.ExtIDs = extIds; entry.ChainId = chainId; return(entry); }
/// <summary> /// Gets a hash of an entry /// </summary> /// <param name="entry">EntryData to be hashed</param> /// <returns>Hash of entry</returns> public static byte[] HashEntry(DataStructs.EntryData entry) { var data = MarshalBinary(entry); var h1 = SHA512.Create().ComputeHash(data); var h2 = new byte[h1.Length + data.Length]; h1.CopyTo(h2, 0); data.CopyTo(h2, h1.Length); var h3 = SHA256.Create().ComputeHash(h2); return(h3); }
/// <summary> /// Passing the first entry of a Chain will get the chainId of that entry. Needs the ExtIDs to do this successfully /// </summary> /// <param name="entry">Entry object</param> /// <returns>ChainID</returns> public static byte[] ChainIdOfFirstEntry(DataStructs.EntryData entry) { var byteList = new List <byte>(); foreach (var ext in entry.ExtIDs) { byteList.AddRange(SHA256.Create().ComputeHash(ext)); } var b = byteList.ToArray(); var chainInfo = SHA256.Create().ComputeHash(b); return(chainInfo); }
/// <summary> /// Helper function of MarshalBinary /// </summary> /// <param name="e"></param> /// <returns></returns> private static byte[] MarshalExtIDsBinary(DataStructs.EntryData e) { var byteList = new List <byte>(); foreach (var exId in e.ExtIDs) { // 2 byte size of ExtID var extLen = Convert.ToInt16(exId.Length); var bytes = BitConverter.GetBytes(extLen); bytes = Bytes.CheckEndian(bytes); byteList.AddRange(bytes); var extIdStr = exId; byteList.AddRange(extIdStr); } return(byteList.ToArray()); }
/// <summary> /// Commits an entry to the Factom blockchain. Must wait 10 seconds if succeeds then call RevealEntry /// </summary> /// <param name="entry">Entry to be committed</param> /// <param name="name">Name of entry credit wallet</param> /// <returns>ChainID of commited Entry</returns> public static byte[] CommitEntry(DataStructs.EntryData entry, string name) { var byteList = new List <byte>(); // 1 byte version byteList.Add(0); // 6 byte milliTimestamp (truncated unix time) byteList.AddRange(Times.MilliTime()); // 32 byte Entry Hash byteList.AddRange(Entries.HashEntry(entry)); // 1 byte number of entry credits to pay var cost = EntryCost(entry); // TODO: check errors byteList.Add(BitConverter.GetBytes(cost)[0]); var com = new WallerCommit(); com.Message = Arrays.ByteArrayToHex(byteList.ToArray()); //Hex encoded string on bytelist var json = JsonConvert.SerializeObject(com); Console.WriteLine("CE Json = " + json); //TODO: Remove var req = new RestRequest("/commit-entry/{name}", Method.POST); req.RequestFormat = DataFormat.Json; req.AddParameter("application/json", json, ParameterType.RequestBody); req.AddUrlSegment("name", name); var resp = StaticValues.clientD.Execute(req); if (resp.StatusCode != HttpStatusCode.OK) { throw new FactomEntryException("Entry Commit Failed. Message: " + resp.ErrorMessage); } //Console.WriteLine("CommitEntry Resp = " + resp.StatusCode + "|" + resp.StatusCode); if (entry.ExtIDs != null) { return(Entries.ChainIdOfFirstEntry(entry)); } return(entry.ChainId); }
/// <summary> /// Creates a new Chain /// </summary> /// <param name="entry">First entry in chain</param> /// <returns></returns> public static ChainType NewChain(DataStructs.EntryData entry) { var c = new ChainType(); c.FirstEntry = entry; var chainHash = new List <byte>(); if (entry.ExtIDs != null) { foreach (var extId in entry.ExtIDs) { var h = SHA256.Create().ComputeHash(extId); chainHash.AddRange(h); } } c.ChainId = SHA256.Create().ComputeHash(chainHash.ToArray()); c.FirstEntry.ChainId = c.ChainId; return(c); }
/// <summary> /// Caculates the cost of an entry /// </summary> /// <param name="entry"></param> /// <returns></returns> public static sbyte EntryCost(DataStructs.EntryData entry) { var entryBinary = Entries.MarshalBinary(entry); var len = entryBinary.Length - 35; if (len > 10240) { //Error, cannot be larger than 10kb throw new ArgumentException("Parameter cannot exceed 10kb of content", nameof(entry)); } var r = len % 1024; var n = (sbyte)(len / 1024); // Capacity of Entry Payment if (r > 0) { n += 1; } if (n < 1) { n = 1; } return(n); }
/// <summary> /// Second and final step in adding an entry to a chain on the factom blockchain /// </summary> /// <param name="entry">Entry to be added</param> /// <returns>Boolean true/false for success/failure</returns> public static bool RevealEntry(DataStructs.EntryData entry) { var rev = new Reveal(); var marshaledEntry = Entries.MarshalBinary(entry); rev.Entry = Arrays.ByteArrayToHex(marshaledEntry); var req = new RestRequest("/reveal-entry/", Method.POST); var json = JsonConvert.SerializeObject(rev); Console.WriteLine("RE Json = " + json); req.RequestFormat = DataFormat.Json; req.AddParameter("application/json", json, ParameterType.RequestBody); IRestResponse resp = StaticValues.clientWallet.Execute <RestRequest>(req); Console.WriteLine("RevealEntry Resp = " + resp.StatusCode + "|" + resp.StatusCode); if (resp.StatusCode != HttpStatusCode.OK) { throw new FactomEntryException("Entry Reveal Failed. Message: " + resp.ErrorMessage); } return(true); }
/// <summary> /// Helper function of MarshalBinary /// </summary> /// <param name="e"></param> /// <returns></returns> private static byte[] MarshalExtIDsSize(DataStructs.EntryData e) { if (e.ExtIDs == null) { short extLen = 0; var bytes = BitConverter.GetBytes(extLen); return(Bytes.CheckEndian(bytes)); } else { var totalSize = 0; foreach (var extElement in e.ExtIDs) { totalSize += extElement.Length + 2; } var extLen = Convert.ToInt16(totalSize); var bytes = BitConverter.GetBytes(extLen); return(bytes); // return Bytes.CheckEndian(bytes); } }
/// <summary> /// Constructs a new EntryData object /// </summary> /// <param name="content">Content of Entry (message pack)</param> /// <param name="extIds">Unique Ids used for first entry of chain to construct a unique chain ID</param> /// <param name="chainId">ChainID of chain</param> /// <returns>EntryData object</returns> public static DataStructs.EntryData NewEntry(byte[] content, byte[][] extIds, byte[] chainId) { var entry = new DataStructs.EntryData(); entry.Content = content; entry.ExtIDs = extIds; entry.ChainId = chainId; return entry; }